【问题标题】:How to download an image with a Java socket HTTP/1.1 request?如何使用 Java 套接字 HTTP/1.1 请求下载图像?
【发布时间】:2015-11-06 22:10:31
【问题描述】:

我正在尝试使用 java.net.Socket 下载图像,而不使用 java.net.URL 和外部库。这是我所拥有的,我不确定什么不起作用。

        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 一个,但我正在尝试创建图像而不是文件。

【问题讨论】:

  • 你试过用 ImageInputStream 代替 InputStream 吗?
  • 为什么?为什么不使用HttpURLConnection 并省去很多麻烦呢? 很多的麻烦。您将不得不处理内容长度、内容编码、HTTP 1.0、...
  • @Luis 如何获得 ImageInputStream?我尝试过对socket.getInputStream 进行类型转换,但这不起作用。
  • @EJP 我可以从套接字获取HttpURLConnection 吗?我想避免麻烦,但我想弄清楚如何使用套接字和 HTTP 请求获取图像。 (我的目标是学习)
  • 不,您不能这样做,您可以从 URL 中获取。关于 ImageInputStream 的建议并没有增加你已经在做的事情。

标签: java image sockets http


【解决方案1】:

使用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();

【讨论】:

  • 此代码假定两个相邻的 CRLF 都在同一个读取中到达。
  • @EJP 缓冲区大小可以根据需要增加。
猜你喜欢
  • 2017-03-17
  • 1970-01-01
  • 2012-10-19
  • 2019-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多