如何使用Java套接字HTTP / 1.1请求下载图像?
我正在尝试使用 java.net.Socket
下载图像而不使用 java.net.URL
和外部库。这是我的,我不确定什么不起作用。
I am trying to download images using java.net.Socket
without java.net.URL
and external libraries. Here is what I have and I am not sure what isn't working.
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
line for:
In order to see what is coming through the stream, exchange the BufferedImage
line for:
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(...)
但是没有用。
Presumably the ImageIO.read(...)
method does not expect the HTTP header in the socket input stream. But I am not sure how to remove the header. I've tried reading the header lines with BufferedReader
and then passing the socket input stream to ImageIO.read(...)
but that did not work.
这是由 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.BufferedImage
或 javafx.scene.image.Image
?后者有一个构造函数,它接受一个输入流,我尝试过,但它不起作用(因为http标题?)。这个问题类似于这个,但我试图创建一个图像而不是文件。
The non-printable characters at the end seems to indicate that what follows the header is an image of some sort. But how can I turn this into a java.awt.image.BufferedImage
or a javafx.scene.image.Image
? The latter has a constructor that takes an input stream, and I've tried that, but it doesn't work (because of the http header?). This question is similar to this one, but I am trying to create an image not a file.
使用 BufferedReader
是错误的,原因有两个:
Using BufferedReader
is a mistake for 2 reasons:
- 它将字节转换为
String
然后将其转换回字节以将其发送到输出流。转换可能(并且可能会)导致数据丢失; - 它解析了太多字节而你无法控制它。
- It converts bytes to a
String
which you then convert back into bytes to send it into the output stream. The conversions may (and probably will) lead to loss of data; - It parses too many bytes and you have no control over it.
你需要手术接近,创建一个所需大小的字节缓冲区,并使用 InputStream
来读取流字节按您自己的条件逐字节。此外,由于您知道HTTP标头结尾是\\\\\\\ n(或13 10 13 10字节),您可以扫描自己的缓冲区以获取此模式并采取相应措施。
You need to approach this surgically, creating a buffer of bytes of the size you want and using an InputStream
to read the stream byte-by-byte on your own terms. Also, since you know that the HTTP header ending is "\r\n\r\n" (or 13 10 13 10 in bytes), you can scan your own buffer for this pattern and act accordingly.
最好的办法是将图像下载到文件中,然后使用ImageIO从本地文件中读取图像。
Your best bet is to download the image to a file and then use the ImageIO to read it from the local file.
// 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();