JAVA - 从网络服务器下载二进制文件(例如 PDF)文件
Posted
技术标签:
【中文标题】JAVA - 从网络服务器下载二进制文件(例如 PDF)文件【英文标题】:JAVA - Download Binary File (e.g. PDF) file from Webserver 【发布时间】:2011-11-17 17:47:48 【问题描述】:我需要从网络服务器下载一个 pdf 文件到我的电脑并保存在本地。
我使用 Httpclient 连接到 webserver 并获取内容正文:
HttpEntity entity=response.getEntity();
InputStream in=entity.getContent();
String stream = CharStreams.toString(new InputStreamReader(in));
int size=stream.length();
System.out.println("stringa html page LENGTH:"+stream.length());
System.out.println(stream);
SaveToFile(stream);
然后我将内容保存在一个文件中:
//check CRLF (i don't know if i need to to this)
String[] fix=stream.split("\r\n");
File file=new File("C:\\Users\\augusto\\Desktop\\progetti web\\test\\test2.pdf");
PrintWriter out = new PrintWriter(new FileWriter(file));
for (int i = 0; i < fix.length; i++)
out.print(fix[i]);
out.print("\n");
out.close();
我也尝试将字符串内容直接保存到文件:
OutputStream out=new FileOutputStream("pathPdfFile");
out.write(stream.getBytes());
out.close();
但结果总是一样的:我可以打开 pdf 文件,但我只能看到白页。错误是否与 pdf 流和 endstream 字符集编码有关? stream 和 endStream 之间的 pdf 内容是否需要以其他方式进行操作?
希望这有助于避免对我想做的事情产生一些误解:
这是我的登录信息(完美运行):
public static void postForm()
String cookie="";
try
System.out.println("POSTFORM ###################################");
String postURL = "http://login.libero.it/logincheck.php";
HttpPost post = new HttpPost(postURL);
post.setHeader("User-Agent", "Chrome/14.0.835.202");
post.setHeader("Referer","http://login.libero.it/?layout=m&service_id=m_mail&ret_url=http://m.mailbeta.libero.it/m/wmm/auth/check");
if(cookieVector.size()>0)
for(int i=0;i<cookieVector.size();i++)
cookie=cookie+cookieVector.elementAt(i).toString().replace("Set-Cookie:", "")+";";
post.setHeader("Cookie",cookie);
//System.out.println("sequenza cookie post:"+cookie);
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("SERVICE_ID", "m_mail"));
params.add(new BasicNameValuePair("LAYOUT", "m"));
params.add(new BasicNameValuePair("DEVICE", ""));
params.add(new BasicNameValuePair("RET_URL","http://m.mailbeta.libero.it/m/wmm/auth/check"));
params.add(new BasicNameValuePair("LOGINID", "secret"));
params.add(new BasicNameValuePair("PASSWORD", "secret"));
UrlEncodedFormEntity ent = new UrlEncodedFormEntity(params,HTTP.UTF_8);
System.out.println("stringa urlPost:"+ent.toString());
post.setEntity(ent);
HttpResponse responsePOST = client.execute(post);
System.out.println("Response postForm: " + responsePOST.getStatusLine());
Header[] allHeaders = responsePOST.getAllHeaders();
String location = "";
for (Header header : allHeaders)
if("location".equalsIgnoreCase(header.getName())) location = header.getValue();
responsePOST.addHeader(header.getName(), header.getValue());
cookieVector.clear();
Header[] headerx=responsePOST.getHeaders("Set-Cookie");
System.out.println("array header:"+headerx.length);
for(int i=0;i<headerx.length;i++)
System.out.println("restituito cookie POST:"+headerx[i].getValue());
cookieVector.add(headerx[i]);
//System.out.println("cookie trovato POST:"+cookieVector.elementAt(i));
//System.out.println("inseriti"+cookieVector.size()+""+"elements");
//HttpEntity resEntity = responsePOST.getEntity();
// populate redirect information in response
//CONTROLLO ESITO LOGIN
if(location.contains("https://login.libero.it/logincheck.php"))
loginError=1;
System.out.println("Redirecting to: " + location);
//EntityUtils.consume(resEntity);
responsePOST.getEntity().consumeContent();
System.out.println("torno a GET:"+"url:"+location+"cookieVector size:"+cookieVector.size());
get(location,"http://login.libero.it/logincheck.php");
catch (IOException ex)
Logger.getLogger(LiberoLoginNew.class.getName()).log(Level.SEVERE, null, ex);
登录后,我可以访问文件的链接(pdf、图像、doc、exc.)。在这种情况下,我们以 pdf 文件为例:
public static void httpConnection(String url,String referer,String cookieAuth)
try
String location="";
String cookie="";
HttpResponse response;
HttpGet get;
HttpEntity respEntity;
Referer=referer;
System.out.println("HTTPCONNECTION ################################");
System.out.println("connessione a:"+url+"............");
get = new HttpGet(url);
if(referer.length()>0)
//httpget.setHeader("Referer",referer );
if(attachmentURL.size()==0)
get.setHeader("User-Agent", "Chrome/14.0.835.202");
else
get.setHeader("Accept-charset", "UTF-8");
get.setHeader("Content-type", "application/pdf");
if(cookieVector.size()>0)
System.out.println("iserisco cookie da vector");
for(int i=0;i<cookieVector.size();i++)
cookie=cookie+cookieVector.elementAt(i).toString().replace("Set-Cookie:", "")+";";
get.setHeader("Cookie", cookie);
else if(cookieAuth.length()>0)
System.out.println("inserisco cookieAuth....");
System.out.println("valore cookieSession:"+cookieAuth);
get.setHeader("Cookie",cookieAuth.replace("Set-Cookie:", "")+";");
response = client.execute(get);
cookieVector.clear();//reset cookie
System.out.println("home get: " + response.getStatusLine());
Header[] headery=response.getAllHeaders();
for(int j=0;j<headery.length;j++)
System.out.println(headery[j].getName()+" "+" VALUE:"+" "+headery[j].getValue());
Header[] headerx=response.getHeaders("Set-Cookie");
System.out.println("array header:"+headerx.length);
System.out.print("httpconnection SERVER HEADERS ###############");
for(int i=0;i<headerx.length;i++)
if("location".equalsIgnoreCase(headerx[i].getName()))
location = headerx[i].getValue();
//ResponseGET.addHeader(headerx[i].getName(), header.getValue());
//System.out.println(headerx[i].getValue());
cookieVector.add(headerx[i]);
//STREAM CONTENT BODY
HttpEntity entity2=response.getEntity();
InputStream in=entity2.getContent(); <==THIS IS THE WAY I GET STREAM RESPONSE
if(attachmentURL.size()>0)
saveAttachment(in);//SAVE FILE <==
else
from(in,htmlpage);//Parse and grab: message title,subject,attachments. If attachments are found then come back here and execute the method saveAttachment.
in.close();
catch (IOException ex)
Logger.getLogger(LiberoLoginNew.class.getName()).log(Level.SEVERE, null, ex);
方法 httpConnection 有效,我可以下载文件!!
服务器响应:
Date VALUE: Fri, 18 Nov 2011 13:09:46 GMT
Server VALUE: Apache/2.2.21 (Unix) mod_jk/1.2.23
Set-Cookie VALUE: MST_PVP=tiQZO3nbl9_5f_OQXtJP32YiqQx_5f_kSh6F6Io7r3xS; Domain=m.libero.it; Path=/
Content-Type VALUE: application/octet-stream
Expires VALUE: Fri, 18 Nov 2011 15:09:46 GMT
Transfer-Encoding VALUE: chunked
响应正文示例:
%PDF-1.7
1 0 obj % entry point
<<
/Type /Catalog
/Pages 2 0 R
> 结束对象
2 0 obj
<<
/Type /Pages
/MediaBox [ 0 0 200 200 ]
/Count 1
/Kids [ 3 0 R ]
>>
endobj
3 0 obj
<<
/Type /Page
/Parent 2 0 R
/Resources <<
/Font <<
/F1 4 0 R
>>
>>
/Contents 5 0 R
>>
endobj
4 0 obj
<<
/Type /Font
/Subtype /Type1
/BaseFont /Times-Roman
>>
endobj
5 0 obj % page content
<<
/Length 44
>>
stream
BT
70 50 TD
/F1 12 Tf
(Hello, world!) Tj
ET
endstream
endobj
xref
0 6
0000000000 65535 f
0000000010 00000 n
0000000079 00000 n
0000000173 00000 n
0000000301 00000 n
0000000380 00000 n
trailer
<<
/Size 6
/Root 1 0 R
>>
startxref
492
%%EOF
现在,让我们从这里开始。 你能告诉我我必须做什么才能将流保存在文件中吗?
########### 解决了:为了从流数据本地保存文件,尊重二进制数据的性质,我这样做了:
public void saveFile(InputStream is)
try
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(new File("test.pdf"))));
int c;
while((c = is.read()) != -1)
out.writeByte(c);
out.close();
is.close();
catch(IOException e)
System.err.println("Error Writing/Reading Streams.");
如果你想要一个更有效的方法,你可以使用 java.IOUtils 并这样做:
public void saveFile(InputStream is)
OutputStream os=new FileOutputStream(new File("test.pdf"));
byte[] bytes = IOUtils.toByteArray(is);
os.write(bytes);
os.close();
【问题讨论】:
How to download and save a file from internet using Java 的可能重复项 “更高效”的方法是废话,因为它将整个文件存储在内存中!尝试使用 2 GB 文件 :) ... 更少的代码!= 更高效。不过,很高兴你解决了你的问题!!干得好! gd1,感谢您的评论。 “它将整个文件存储在内存中!”,是的,这是真的。但取决于应用程序。在这种情况下,我们讨论的是电子邮件附件,附件大小超过 10 MB 的情况很少见。 :) 不过我很欣赏你的小费,再次感谢! :) 【参考方案1】:从不将二进制数据存储到String
。
永远不要对二进制数据使用PrintWriter
。
从不逐行写入二进制文件。
我不想苛刻或不礼貌,但这三个永远必须在你的脑海中扎根! :)
您可以查看this page 获取有关如何下载二进制文件的示例。我不喜欢这个例子,因为它将整个文档缓存在内存中(如果它的大小是 5GB 会发生什么?)但是你可以从这个开始。 :)
【讨论】:
我试过你的例子(我几天前已经试过了),但是没用。在这种情况下 pdf 文件不会打开。 您不应该尝试复制和粘贴其他人编写的代码片段,希望您随机找到正确的组合。一旦您理解了问题(下载二进制文件,而不是文本文件),您应该使用 Java 文档中的示例,以便找到正确且适合您需求的解决方案,但由您编写。逐行编写一些代码,调试每一行,并为我们创建一个 SSCE (sscce.org) 因此,不要尝试您在 Internet 上找到的示例,也不要尝试在您的程序中找到 hittledown 提供的示例,而是在一个适当的、单独的测试用例中,真正向我们展示它在哪里以及如何失败。更多,请了解 Java 字节流,因为如果您认为可以逐行编写二进制文件,那么即使您成功地使程序以某种尴尬的方式运行,您也会遇到越来越多的问题。解决问题! 显然,我将示例代码改编为我的代码,我不是超级傻瓜!!!.. :)) 我已经使用 urlConnection 库测试了其他脚本(如 hartleMan 示例)下载其他不需要登录的网络服务器上的其他 pdf 文件,一切都很好。(pdf 已成功打开)我会在单独的测试用例中尝试你和 hartleman 的示例,但我不能,因为要复制我需要的真实测试首先登录到特定的网络服务器,然后下载 pdf 文件。但是用 UrlConnection 管理 cookie 太难了! 好的。所以问题在这里无法解决,因为我们不知道您的代码在登录部分可能会搞砸什么,并且“我尝试了您的示例(我几天前已经尝试过)但是 nada”至少具有误导性,不要你不觉得吗?你不需要 S.O.,你基本上必须调试你的代码。如果您的登录部分不起作用,请停止告诉您有下载 PDF 问题并专注于它。但是,如果您继续认为可以为它们使用 PrintWriter,那么您显然也有下载 PDF 的问题。 :)【参考方案2】:使用 apache FileUtils。我用一个小的 PDF 和一个 60 兆的 JAR 进行了尝试。效果很好!
import java.io.File;
import java.io.IOException;
import java.net.URL;
import org.apache.commons.io.FileUtils;
String uri = "http://localhost:8080/PMInstaller/f1.pdf";
URL url = new URL(uri);
File destination = new File("f1.pdf");
FileUtils.copyURLToFile(url, destination);
【讨论】:
【参考方案3】:你不能直接拿链接吗?
public static void downloadFile(URL from, File to, boolean overwrite) throws Exception
if (to.exists())
if (!overwrite)
throw new Exception("File " + to.getAbsolutePath() + " exists already.");
if (!to.delete())
throw new Exception("Cannot delete the file " + to.getAbsolutePath() + ".");
int lengthTotal = 0;
try
HttpURLConnection content = (HttpURLConnection) from.openConnection();
lengthTotal = content.getContentLength();
catch (Exception e)
lengthTotal = -1;
int lengthSoFar = 0;
InputStream is = from.openStream();
FileOutputStream fos = new FileOutputStream(to);
int lastUpdate = 0;
int c;
while ((c = is.read()) != -1)
fos.write(c);
is.close();
fos.close();
【讨论】:
逐字节阅读是疯狂的。但是 +1 因为总体意图是好的。 你是对的......这是为小文件开发的,并且有一个精确的进度条。不管你信不信,我最近将它与使用 nio 下载文件的速度进行了比较,在其中你只需连接两个流,它需要相同的时间...... 是的,网络可能比我们可能编写的任何未经优化的代码“更糟糕”。我们的机器变得更好,而我们的网络变得更糟。您应该在 100Mbit LAN 上尝试使用 5 GB 的文档,它最终会有所作为... hurtledown,我必须先使用一系列 cookie 登录到网络服务器,然后才能下载文件。有什么建议吗? 你正在编写一个 HTTP 机器人。这将需要一些努力,因为它没有单行提示。对于 cookie:download.oracle.com/javase/tutorial/networking/cookies/… 要登录,您可能需要在登录页面上发布凭据。见:bytestrike.blogspot.com/2008/05/…【参考方案4】:让jsoup 努力将响应下载为字节。
Response response= Jsoup.connect(location)
.ignoreContentType(true)
.userAgent("Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0")
.referrer("http://www.google.com")
.timeout(12000)
.execute();
使用apache commons FileUtil 写入字节。
FileUtils.writeByteArrayToFile(new File(path), response.bodyAsBytes());
【讨论】:
以上是关于JAVA - 从网络服务器下载二进制文件(例如 PDF)文件的主要内容,如果未能解决你的问题,请参考以下文章
sh 从终端/ shell /命令行/命令提示符下载Oracle网站上的JDK / JRE / Java二进制文件的脚本