背景:
使用url通过httpClient的get方法,从oss下载pdf文件,被下载的文件通过浏览器能正常查看和下载
问题:
偶尔会出现一个下载文件为0KB的情况
代码:
GetMethod httpGet = new GetMethod(strUrlPath);
InputStream in = null;
FileOutputStream out = null;
try {
HttpClient httpClient = new HttpClient();
httpClient.executeMethod(httpGet);
in = httpGet.getResponseBodyAsStream();
out = new FileOutputStream(new File(strLocalPath));
byte[] b = new byte[1024];
int len = 0;
while ((len = in.read(b)) != -1) {
out.write(b, 0, len);
}
in.close();
out.close();
} catch (Exception e) {
log.error("httpClient下载文件失败", e);
} finally {
httpGet.releaseConnection();
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (Exception e1) {
log.error("文件流信息关闭失败", e1);
}
}
分析过程:
- 因为 FileOutputStream 没有调用flush方法,认为是数据未从缓存区刷入到系统,就关闭了输出流,导致文件为0KB。结果发现分析错误,通过查看源码发现 flush 方法是 OutputStream 的方法,是个空实现。FileOutputStream 继承了 OutputStream 并未重写其 flush 方法,所以 FileOutputStream 是直接写入系统缓存。
- 通过查看资料,了解到输出流持久化文件是一个异步的过程。首先输出流将字节写入系统缓存,系统缓存会直接返回写入结果。然后系统缓存会排队将内容持久化。那我们可以将该过程变为一个同步过程么?答案是可以的。通过调用输出流的 FileOutputStream.getFD().sync() 方法,同步等待文件持久化之后返回结果。至此问题解决。
注意:使用 FileOutputStream.getFD().sync() 同步方法,对文件写入的性能影响较大,是否需要使用还需要根据具体业务进行权衡。