用httpClient模拟浏览器下载文件的代码,网上是很多的,自己copy了一个就高兴的用起来,下载了几百个文件之后,MD发现所有下载的文件都是损坏的、根本打不开,这TM就尴尬了啊,用浏览器下载是没问题的啊。
下面看一下当时用的代码:
private static void down(String url, String path, int index) {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
// 这个地方根据浏览器里的内容复制过来
httpGet.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
httpGet.setHeader("Connection", "keep-alive");
httpGet.setHeader("Cookie", "XXXXX浏览器里的cookie复制过来就可以");
httpGet.setHeader("Host", "XX.XX.XX");
try {
CloseableHttpResponse response = httpclient.execute(httpGet);
HttpEntity httpEntity = response.getEntity();
InputStream in = httpEntity.getContent();
String fileName = getFileName(response);
File file = new File(path + fileName);
FileOutputStream fOut = new FileOutputStream(file);
// IOUtils.copy(in,fOut);
byte[] buffer = new byte[4*1024];
while (in.read(buffer) != -1) {
fOut.write(buffer);
}
fOut.flush();
in.close();
fOut.close();
System.out.println(index+"=============ok================" + path + fileName);
} catch (IOException e) {
e.printStackTrace();
}
}
坑就坑在这个地方:
while (in.read(buffer) != -1) {
fOut.write(buffer);
}
httpClient传输过来的流不一定会每次把 byte[] buffer写满,这种写法在本地复制文件的时候是可以的,但是网络流上就会出大问题,也就是out.write()的起终点没有显示的指定而是默认取buffer.length,每次缓存基本上都是读不满的,所以导致写入大量的空流到文件中。这也长了个教训,以后在调用API的时候能显示指定的就不要用默认值,你不知道会出什么问题。
正确的写法是这样的:
int n = 0;
while ((n=in.read(buffer)) != -1) {
fOut.write(buffer,0,n);
}
或者有工具类可以用 IOUtils.copy(in,out);这个方法里的实现是一样的。
另外:获取文件名的方法附上:
private static String getFileName(CloseableHttpResponse response) {
String fileName = "";
Header header = response.getFirstHeader("Content-disposition");
//System.out.println(JSONObject.toJSONString(response));
//System.out.println(JSONObject.toJSONString(header));
if (header != null) {
HeaderElement[] headerElements = header.getElements();
NameValuePair nameValuePair = headerElements[0].getParameterByName("filename");
fileName = nameValuePair.getValue();
//System.out.println("============fileName==============" + fileName);
//System.out.println(nameValuePair.getName());
}
return fileName;
}