1 场景:通过url读取远程图片文件并返回一个byte[],将来这些byte可以通过HttpClient上传到远程机器。
1.1 如果将这些的byte[]上传之后,发现图片只能显示一部分。最终怀疑是上传工具HttpClient由于网络的问题导致上传不全,但多次尝试发现问题可能是读url获取byte[]的方法有问题。
错误:使用BufferedInputStream和read方法
private byte[] readFileImage(String sImageUrl) throws IOException, ServiceException { URL url = new URL(sImageUrl); URLConnection urlConnection = url.openConnection(); BufferedInputStream bufferedInputStream = new BufferedInputStream(urlConnection.getInputStream()); int len = bufferedInputStream.available();//这种方式感觉对本地文件是可用的。但是对于远程文件,available并不能返回真实的文件大小。文件大小需要你读一部分累加一部分直到读完才可以。 if (len > 1024 * 1224 * 5) { throw new ServiceException("The post image is larger than 5M"); } byte[] bytes = new byte[len]; int r = bufferedInputStream.read(bytes); if (len != r) { bytes = null; bufferedInputStream.close(); throw new IOException("读取文件不正确"); } bufferedInputStream.close(); return bytes; }
1.2 使用ByteArrayOutputStream和toByteArray方法
1.2.1 错误的方式:写到ByteArrayOutputStream时,如果没有按照读到的长度去写,而是按照buffer.length去写。这样会导致图片变花。
private byte[] getImageByteArray(InputStream ins) { byte[] buffer = new byte[1024]; System.out.println(buffer.length); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { boolean isComplete = false; while (!isComplete) { if (ins.read(buffer) != -1) { baos.write(buffer); // 与baos.write(buffer, 0, // buffer.length);等效。 // baos.flush();//图片变花与flush何处调用无关。 } else { isComplete = true; } } baos.flush();//对于dest是ByteArrayOutputStream,是否flush无所谓。 } catch (Exception e) { Logging.DAODAO_SNS.error(e); } byte[] res = baos.toByteArray(); try { ins.close(); baos.close(); } catch (IOException e2) { Logging.DAODAO_SNS.error(e2); } return res; }
1.2.2 正确的写入:将read每次从src读入到buffer中的字节数精确写到dest,如果dest对应File,flush会起作用。
private byte[] getImageByteArray(InputStream ins) { byte[] buffer = new byte[1024]; ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { int length = -1; while ((length = ins.read(buffer)) != -1) { baos.write(buffer, 0, length); // baos.flush(); } } catch (IOException e) { Logging.DAODAO_SNS.error(e); } byte[] res = baos.toByteArray(); try { ins.close(); baos.close(); } catch (IOException e2) { Logging.DAODAO_SNS.error(e2); } return res; }
2. 场景: 从File中读取byte[]
public static byte[] readFileImage(String filename)throws IOException{ BufferedInputStream bufferedInputStream=new BufferedInputStream( new FileInputStream(filename)); int len =bufferedInputStream.available(); byte[] bytes=new byte[len]; int r=bufferedInputStream.read(bytes); if(len !=r){ bytes=null; throw new IOException("读取文件不正确"); } bufferedInputStream.close(); return bytes; }
3.关于OutputStream的flush操作:
Flush function is to explicitly ask Java program to write something into the disk, which means doing IO. Say if we are using a bufferedOutputStream, when we do write("something"), it is stored in the buffer, but not in the disk yet. When you call flush, it will write the stuffs in your buffer to the disk. Usually, you don't need to call the flush by yourself, cause it requires IO and lower down the speed. The stream will automatically flush when the buffer is full. Unless you want some contents being updated to the disk, you can use flush.
4.远程文件的读取不要使用available分配buffer
Returns an estimate of the number of bytes that can be read (or skipped over) from this input stream without blocking by the next invocation of a method for this input stream. The next invocation might be the same thread or another thread. A single read or skip of this many bytes will not block, but may read or skip fewer bytes.
Note that while some implementations of InputStream will return the total number of bytes in the stream, many will not. It is never correct to use the return value of this method to allocate a buffer intended to hold all data in this stream.
In short, InputStream.available()
is not half as useful as you think it is.
If you need to detect the end of the stream, read()
from it and it detect if the result is -1
. Do not use available()
.