排球 – 直接下载到文件(内存字节数组中没有)

我在我正在使用Android的项目中使用Volley作为我的networking堆栈。 我的部分要求是下载可能非常大的文件并将其保存在文件系统中。

我一直在看volley的实现,似乎唯一的方法是将整个文件下载到一个潜在的庞大的字节数组中,然后把这个字节数组的处理延迟到一些callback处理程序。

由于这些文件可能非常大,我担心在下载过程中出现内存不足错误。

有没有办法让volley将来自httpinputstream的所有字节直接处理成文件输出stream? 或者这将需要我实现我自己的networking对象?

我找不到任何关于这个在线的材料,所以任何build议,将不胜感激。

Solutions Collecting From Web of "排球 – 直接下载到文件(内存字节数组中没有)"

好的,所以我想出了一个解决scheme,包括编辑Volley本身。 这是一个步行通过:

networking响应不能容纳一个字节数组了。 它需要保存一个inputstream。 这样做会立即中断所有的请求实现,因为它们依赖于拥有公共字节数组成员的NetworkResponse。 我发现处理这种侵入性最小的方法是在NetworkResponse中添加一个“toByteArray”方法,然后做一些重构,使得对字节数组的任何引用都使用此方法,而不是删除的字节数组成员。 这意味着inputstream到字节数组的转换发生在响应分析过程中。 我不完全确定这是什么样的长期影响,所以一些unit testing/社区的投入将是一个巨大的帮助。 代码如下:

public class NetworkResponse { /** * Creates a new network response. * @param statusCode the HTTP status code * @param data Response body * @param headers Headers returned with this response, or null for none * @param notModified True if the server returned a 304 and the data was already in cache */ public NetworkResponse(int statusCode, inputStream data, Map<String, String> headers, boolean notModified, ByteArrayPool byteArrayPool, int contentLength) { this.statusCode = statusCode; this.data = data; this.headers = headers; this.notModified = notModified; this.byteArrayPool = byteArrayPool; this.contentLength = contentLength; } public NetworkResponse(byte[] data) { this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false); } public NetworkResponse(byte[] data, Map<String, String> headers) { this(HttpStatus.SC_OK, data, headers, false); } /** The HTTP status code. */ public final int statusCode; /** Raw data from this response. */ public final InputStream inputStream; /** Response headers. */ public final Map<String, String> headers; /** True if the server returned a 304 (Not Modified). */ public final boolean notModified; public final ByteArrayPool byteArrayPool; public final int contentLength; // method taken from BasicNetwork with a few small alterations. public byte[] toByteArray() throws IOException, ServerError { PoolingByteArrayOutputStream bytes = new PoolingByteArrayOutputStream(byteArrayPool, contentLength); byte[] buffer = null; try { if (inputStream == null) { throw new ServerError(); } buffer = byteArrayPool.getBuf(1024); int count; while ((count = inputStream.read(buffer)) != -1) { bytes.write(buffer, 0, count); } return bytes.toByteArray(); } finally { try { // Close the InputStream and release the resources by "consuming the content". // Not sure what to do about the entity "consumeContent()"... ideas? inputStream.close(); } catch (IOException e) { // This can happen if there was an exception above that left the entity in // an invalid state. VolleyLog.v("Error occured when calling consumingContent"); } byteArrayPool.returnBuf(buffer); bytes.close(); } } } 

然后为了准备NetworkResponse,我们需要编辑BasicNetwork来正确创buildNetworkResponse(在BasicNetwork.performRequest ):

 int contentLength = 0; if (httpResponse.getEntity() != null) { responseContents = httpResponse.getEntity().getContent(); // responseContents is now an InputStream contentLength = httpResponse.getEntity().getContentLength(); } ... return new NetworkResponse(statusCode, responseContents, responseHeaders, false, mPool, contentLength); 

而已。 一旦networking响应内的数据是一个inputstream,我可以build立自己的请求,它可以直接parsing成一个文件输出stream,只有一个小的内存缓冲区。

从最初的一些testing来看,这似乎没有损害其他组件,但是像这样的变化可能需要一些更密集的testing和同行审查,所以我会留下这个答案没有标记为正确的,直到更多的人的权衡,或者我认为它足够强大,可以依靠。

请随时评论这个答案和/或发表回答你自己。 这在Volley的devise中感觉就像是一个严重的缺陷,如果你看到这个devise有缺陷,或者你可以自己想一些更好的devise,那么我认为这对大家都有好处。