2015-11-06 55 views
1

我正在尝试使用java.net.Socket而不是java.net.URL和外部库下载图像。这是我的,我不知道什么是不工作。如何使用Java套接字HTTP/1.1请求下载图像?

 String domain = "www.manchester.edu"; 
     String path = "/images/default-source/default-album/slide1.jpg"; 
     Socket socket = new Socket(domain,80); 

     PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))); 
     out.println("" + 
       "Get "+path+" HTTP/1.1\n" + 
       "Host: "+domain+"\n"+ 
       ""); 
     out.println(); 
     out.flush(); 

     BufferedImage image = ImageIO.read(socket.getInputStream()); 

为了看看经过流过来,交换BufferedImage线:

BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
    String inputLine; 
    while ((inputLine = in.readLine()) != null && inputLine.trim() != "0") { 
     System.out.println(inputLine); 
    } 

想必ImageIO.read(...)方法并不指望插座输入流中的HTTP头。但我不知道如何删除标题。我试过用BufferedReader读取标题行,然后将套接字输入流传递到ImageIO.read(...),但那不起作用。

这里是由BufferedReader打印的字符串:

HTTP/1.1 200 OK 
Cache-Control: public, max-age=7776000 
Content-Length: 96876 
Content-Type: image/jpeg 
Expires: Thu, 04 Feb 2016 21:36:46 GMT 
Last-Modified: Tue, 15 Sep 2015 14:23:40 GMT 
Server: Microsoft-IIS/8.5 
content-disposition: inline; filename=slide1.jpg 
X-AspNet-Version: 4.0.30319 
X-Powered-By: ASP.NET 
Date: Fri, 06 Nov 2015 21:36:46 GMT 

����... 

的非打印字符在端似乎表明什么跟随报头是某种的图像。但是我怎样才能把它变成java.awt.image.BufferedImagejavafx.scene.image.Image?后者有一个接受输入流的构造函数,我试过了,但它不起作用(因为http头?)。这个问题类似于this之一,但我试图创建一个图像而不是文件。

+0

您是否尝试过使用的ImageInputStream代替InputStream的? –

+2

为什么?为什么不使用'HttpURLConnection'并节省很多麻烦?麻烦一大堆。你将不得不处理内容长度,内容编码,HTTP 1.0,... – EJP

+0

@Luis如何获得ImageInputStream?我试过类型铸造'socket.getInputStream',但这是行不通的。 – NonlinearFruit

回答

2

使用BufferedReader是有两个原因的错误:

  1. 它可以转换字节为String,你再转换回字节将其发送到输出流。转换可能(可能会)导致数据丢失;
  2. 它分析太多字节,你无法控制它。

您需要手术处理这个,创造你想要的大小字节的缓冲区,并使用InputStream读取流逐字节你自己的条件。此外,由于您知道HTTP头结尾是“\ r \ n \ r \ n”(或13 10 13 10字节),因此您可以扫描您自己的缓冲区以查找此模式并采取相应措施。

最好的办法是将图像下载到文件中,然后使用ImageIO从本地文件读取图像。

下面的代码,将允许您下载通过切割头的图像文件(或任何其他文件):

// Initialize the streams. 
    final FileOutputStream fileOutputStream = new FileOutputStream(file); 
    final InputStream inputStream = socket.getInputStream(); 

    // Header end flag. 
    boolean headerEnded = false; 

    byte[] bytes = new byte[2048]; 
    int length; 
    while ((length = inputStream.read(bytes)) != -1) { 
     // If the end of the header had already been reached, write the bytes to the file as normal. 
     if (headerEnded) 
      fileOutputStream.write(bytes, 0, length); 

     // This locates the end of the header by comparing the current byte as well as the next 3 bytes 
     // with the HTTP header end "\r\n\r\n" (which in integer representation would be 13 10 13 10). 
     // If the end of the header is reached, the flag is set to true and the remaining data in the 
     // currently buffered byte array is written into the file. 
     else { 
      for (int i = 0; i < 2045; i++) { 
       if (bytes[i] == 13 && bytes[i + 1] == 10 && bytes[i + 2] == 13 && bytes[i + 3] == 10) { 
        headerEnded = true; 
        fileOutputStream.write(bytes, i+4 , 2048-i-4); 
        break; 
       } 
      } 
     } 
    } 
    inputStream.close(); 
    fileOutputStream.close(); 
+0

此代码假定两个相邻的CRLF全部到达相同的读取。 – EJP

+0

@EJP缓冲区大小可根据需要增加。 – Alexay