使用 java HTTP POST 连接发送图像文件

2022-09-02 09:59:56

我正在尝试使用Java HTTP POST请求将图像发送到网站。

我正在使用这里使用的基本代码 将文件从Java客户端上传到HTTP服务器

这是我的修改:

String urlToConnect = "http://localhost:9000/upload";
File fileToUpload = new File("C:\\Users\\joao\\Pictures\\bla.jpg");
String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value.

URLConnection connection = new URL(urlToConnect).openConnection();
connection.setDoOutput(true); // This sets request method to POST.
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
PrintWriter writer = null;
try {
    writer = new PrintWriter(new OutputStreamWriter(connection.getOutputStream()));
    writer.println("--" + boundary);
    writer.println("Content-Disposition: form-data; name=\"picture\"; filename=\"bla.jpg\"");
    writer.println("Content-Type: image/jpeg");
    writer.println();
    BufferedReader reader = null;
    try {
        reader = new BufferedReader(new InputStreamReader(new FileInputStream(fileToUpload)));
        for (String line; (line = reader.readLine()) != null;) {
            writer.println(line);
        }
    } finally {
        if (reader != null) try { reader.close(); } catch (IOException logOrIgnore) {}
    }
    writer.println("--" + boundary + "--");
} finally {
    if (writer != null) writer.close();
}

// Connection is lazily executed whenever you request any status.
int responseCode = ((HttpURLConnection) connection).getResponseCode();
System.out.println(responseCode); // Should be 200

我最终得到了一个200响应代码,但图像是错误的,就像随机颜色一样,这让我认为这是字符编码的错误。我尝试使用原始示例中的UTF-8,但这只会创建一个损坏的图像。

我也100%确定这不是服务器端问题,因为我可以使用REST客户端,如Advanced Rest Client/Postman,他们可以毫无问题地发送图像。

你能帮我找出问题所在吗?谢谢。


答案 1
import java.io.File;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ContentBody;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.util.EntityUtils;


public class PostFile {
  public static void main(String[] args) throws Exception {
    HttpClient httpclient = new DefaultHttpClient();
    httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);

    HttpPost httppost = new HttpPost("http://localhost:9000/upload");
    File file = new File("C:\\Users\\joao\\Pictures\\bla.jpg");

    MultipartEntity mpEntity = new MultipartEntity();
    ContentBody cbFile = new FileBody(file, "image/jpeg");
    mpEntity.addPart("userfile", cbFile);


    httppost.setEntity(mpEntity);
    System.out.println("executing request " + httppost.getRequestLine());
    HttpResponse response = httpclient.execute(httppost);
    HttpEntity resEntity = response.getEntity();

    System.out.println(response.getStatusLine());
    if (resEntity != null) {
      System.out.println(EntityUtils.toString(resEntity));
    }
    if (resEntity != null) {
      resEntity.consumeContent();
    }

    httpclient.getConnectionManager().shutdown();
  }
}

使用 HttpClient 计算此代码。使用稳定的库总是比从头开始处理更好,除非有一些东西要以自定义方式处理。


答案 2

今天我遇到了同样的问题,我写了一个小nodejs服务器只支持两条路由,上传和下载图像。

客户端应该是一个java类,它通过HTTP POST多部分/表单数据标准将图像有效负载发送到服务器。

如果你想知道为什么HTTP POST multipart/form-data,请查看Ciro Santilli在这篇文章中的答案:enctype='multipart/form-data'是什么意思?

幸运的是,我发现了这个很好且非常好的示例代码:

https://www.techcoil.com/blog/how-to-upload-a-file-via-a-http-multipart-request-in-java-without-using-any-external-libraries/

它展示了我们如何在没有任何外部lib的情况下构建多部分http主体的有效载荷,从我的角度来看,只有很少的限制是,它只处理一个具有一个文件的多部分主体。


因为我没有HTML页面来嗅探生成的POST有效负载,所以我使用python来生成它并通过wireshark嗅探它。

Python3 代码:

import requests
posturl = 'http://<server>:<port>/<path>'
files = {'image' : open('<file>', 'rb')}
r = requests.post(posturl, files = files)

仅需要注意:如果我们使用字典从请求库中定义参数文件,它将生成一个 mulipart/form-data 内容。http://docs.python-requests.org/en/master/user/advanced/#post-multiple-multipart-encoded-files

Wireshark显示所有内容都非常清晰,最后我最终用这个来发送java:

HttpURLConnection conn =
        (HttpURLConnection) new URL("http://<server>:<port>/<path>")).openConnection();

// some arbitrary text for multitext boundary
// only 7-bit US-ASCII digits max length 70
String boundary_string = "some radom/arbitrary text";

// we want to write out
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.addRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary_string);

// now we write out the multipart to the body
OutputStream conn_out = conn.getOutputStream();
BufferedWriter conn_out_writer = new BufferedWriter(new OutputStreamWriter(conn_out));
// write out multitext body based on w3 standard
// https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
conn_out_writer.write("\r\n--" + boundary_string + "\r\n");
conn_out_writer.write("Content-Disposition: form-data; " +
        "name=\"image\"; " +
        "filename=\""+ <File class instance>.getName() +"\"" +
        "\r\n\r\n");
conn_out_writer.flush();

// payload from the file
FileInputStream file_stream = new FileInputStream(<File class instance>);
// write direct to outputstream instance, because we write now bytes and not strings
int read_bytes;
byte[] buffer = new byte[1024];
while((read_bytes = file_stream.read(buffer)) != -1) {
conn_out.write(buffer, 0, read_bytes);
}
conn_out.flush();
// close multipart body
conn_out_writer.write("\r\n--" + boundary_string + "--\r\n");
conn_out_writer.flush();

// close all the streams
conn_out_writer.close();
conn_out.close();
file_stream.close();
// execute and get response code
conn.getResponseCode();

要从POST获得响应,只需读取通过getInputStream()访问的输入流,链接中截取的代码。