在某些業(yè)務(wù)場景中,需要判斷獲取到的InputStream是否為空。
InputStream沒有接口去判斷是否為空或者獲取其大小,本文會(huì)列出項(xiàng)目中見到的一些解決方案。
輸入流可用
有些同學(xué)直接把InputStream.available當(dāng)成流的實(shí)際大小,比如下載的時(shí)候就直接把InputStream.available設(shè)置為Content-Length,這個(gè)是很大的錯(cuò)誤,available方法注釋里明確寫到:
返回可從此輸入流讀取(或跳過)的字節(jié)數(shù)估計(jì)值estimate是什么意思?怎么讀,而不會(huì)被下次調(diào)用此輸入流的方法阻塞。
估計(jì)是一個(gè)大概的估計(jì),并不代表流的實(shí)際大小,如果是FileInputStream的話,我的測試結(jié)果和文件大小是一致的,但是有可能不同的操作系統(tǒng),不同品牌的JDK版本可能會(huì)產(chǎn)生不同的結(jié)果。
如果你的業(yè)務(wù)可以接受這個(gè)估算值,那么就可以用它來判斷流是否為空。
toByteArray 轉(zhuǎn)換字節(jié)數(shù)組
IOUtils.toByteArray(InputStream) 轉(zhuǎn)為字節(jié)數(shù)組,由于通過流無法獲取大小,所以我就繞了個(gè)彎子,把流轉(zhuǎn)為字節(jié)數(shù)組,這樣之后不就為所欲為了了嗎?
這樣確實(shí)能拿到值,而且能準(zhǔn)確判斷是否為空。但是如果一次性把流讀成字節(jié)數(shù)組,你不覺得內(nèi)存可能受不了?
InputStream其實(shí)就是連接自來水廠的水管,不管是一噸水還是十噸水,這個(gè)InputStream占用的內(nèi)存基本是固定的。用專業(yè)的話來說,它的空間復(fù)雜度是O(1)。如果把它轉(zhuǎn)化成字節(jié)數(shù)組,就相當(dāng)于把你家里的十噸水全部存起來了。數(shù)據(jù)量少的話還好,但是如果遇到大數(shù)據(jù)量或者高并發(fā)的話,內(nèi)存就會(huì)立馬爆掉。
聽我的建議,除非你能清楚地評(píng)估沒有 OOM 風(fēng)險(xiǎn),否則不要轉(zhuǎn)換為字節(jié)數(shù)組。
讀取第一個(gè)字節(jié)
既然只需要判斷是否為空,那我何必這么麻煩呢?InputStream不是有read方法嗎?難道不能先讀取第一個(gè)字節(jié),然后判斷是否為空嗎?
前面我們說過,InputStream 就像是一根水管,每讀到一個(gè)字節(jié),流中就會(huì)少一個(gè)字節(jié)。它就像一個(gè)送貨員網(wǎng)校頭條,你問他湯咸不咸estimate是什么意思?怎么讀,他喝了一口說:湯不錯(cuò),不咸。如果你喝到一半湯,你會(huì)是什么感覺?雖然 InputStream 提供了 reset 方法,但是默認(rèn)會(huì)拋出異常。并不是所有的流都可以 reset,就像愛情有多少次可以重來一樣。
????public?synchronized?void?reset()?throws?IOException?{
????????throw?new?IOException("mark/reset?not?supported");
????}
PushbackInputStream 的終極解決方案
PushbackInputStream,顧名思義就是可以回滾的流,你可以用它來包裝原來的流,這樣就可以檢查流是否為空。
????/**
?????*?檢查輸入流是否為空,并返回包裝后的流
?????*?請(qǐng)注意,原始流已經(jīng)被讀了一個(gè)字節(jié),后續(xù)不能直接對(duì)原始流進(jìn)行讀取
?????*
?????*?@param?inputStream?inputStream
?????*?@return?包裝之后的流,后續(xù)操作的都是這個(gè)流
?????*/
????public?InputStream?checkStreamIsNotEmpty(InputStream?inputStream)?throws?IOException,
????????????EmptyInputStreamException?{
????????AssertKit.isNull(inputStream,?"流不能為null");
????????PushbackInputStream?pushbackInputStream?=?new?PushbackInputStream(inputStream);
????????int?b?=?pushbackInputStream.read();
????????if?(b?==?-1)?{
????????????throw?new?EmptyInputStreamException("這個(gè)流是空的,啥也沒有。?"?+?inputStream);
????????}
????????pushbackInputStream.unread(b);
????????return?pushbackInputStream;
????}