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)文件的主要内容,如果未能解决你的问题,请参考以下文章

java如何实现从服务器下载已经生成好的excel文件

使用 Java 从 Github 下载二进制文件

AF网络。检查所有操作队列的下载进度

Java TCP 网络通信编程

通过 Web 服务上传文件

sh 从终端/ shell /命令行/命令提示符下载Oracle网站上的JDK / JRE / Java二进制文件的脚本