如何构造正确的 MultipartEntity 以在 java 中发送 multipart/related 请求?

How to construct correct MultipartEntity to send a multipart/related request in java?

我想从本地计算机发送到 OCR 图像,而不是一些带有图像的远程 url(这种情况下效果很好)。但问题是,我不知道如何正确构造多部分实体的有效载荷。我的负载应该如下所述。

这就是 documentation of API 中描述的内容。解码multipart/related数据中直接给出的图像数据。顺序很重要,第一部分应该是 JSON,它告诉它使用哪个 OCR 引擎。 JSON 的架构记录在 /ocr 端点中。在这种情况下,JSON 的 img_url 参数将被忽略。

图像附件应该是 second 部分,它应该适用于任何图像内容类型(例如,image/png、image/jpg 等) .

   Request (multipart/related; boundary=---BOUNDARY)

    -----BOUNDARY
    Content-Type: application/json

    {"engine":"tesseract"}
    -----BOUNDARY

    -----BOUNDARY
    Content-Disposition: attachment;
    Content-Type: image/png
    filename="attachment.txt".

    PNGDATA.........
    -----BOUNDARY

这是我试过的方法。要执行 multipart/related 请求,我正在使用 org.apache.httpcomponents

    CloseableHttpClient httpClient = HttpClients.createDefault();

    MultipartEntityBuilder multipartEntityBuilder =
            MultipartEntityBuilder.create().setBoundary(BOUNDARY).setContentType(ContentType.APPLICATION_JSON).addTextBody("engine", "tesseract")
                                                    .setBoundary(BOUNDARY).setBoundary(BOUNDARY);

    multipartEntityBuilder.addBinaryBody("file_upload", new File(fileTextPath), ContentType.create(CONTENT_TYPE), fileTextPath).setBoundary(BOUNDARY);

    HttpEntity entity = multipartEntityBuilder.build();


    HttpPost httpPost = new HttpPost(URL);
    httpPost.setHeader(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE_MULTIPART+";boundary="+BOUNDARY);
    httpPost.setEntity(entity);

几周前我解决了这个问题,为了方便起见,我为这两种情况(远程和文件上传)创建了一个lightweight java web application

您可以找到上述问题的具体答案here。多部分请求的源代码如下所示:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

public class MultipartUtility {
    private static final Logger logger = LoggerFactory.getLogger(MultipartUtility.class);

    private final String boundary = UUID.randomUUID().toString();
    private static final String LINE_FEED = "\r\n";
    private HttpURLConnection httpConn;
    private String charset;
    private OutputStream outputStream;
    private PrintWriter writer;

    /**
     * This constructor initializes a new HTTP POST request with content type
     * is set to multipart/form-data
     *
     * @param requestURL
     * @param charset
     * @throws IOException
     */
    public MultipartUtility(String requestURL, String charset) {
        this.charset = charset;

        try {
            URL url = new URL(requestURL);
            httpConn = (HttpURLConnection) url.openConnection();
            httpConn.setUseCaches(false);
            httpConn.setDoOutput(true); // indicates POST method
            httpConn.setDoInput(true);
            httpConn.setRequestProperty("Content-Type", "multipart/related; boundary=\"" + boundary + "\"");
            httpConn.setRequestProperty("Accept-Encoding", "gzip");
            outputStream = httpConn.getOutputStream();
            //outputStream = System.out;
            writer = new PrintWriter(new OutputStreamWriter(outputStream, charset), true);
        } catch (IOException ex) {
            logger.error("Error during creation of MultiPart: ", ex);
        }
    }

    /**
     * Adds a form field to the request
     */
    public void addFormField(String jsonBody) {
        writer.append("--").append(boundary).append(LINE_FEED);
        writer.append("Content-Type: application/json;").append(LINE_FEED);
        writer.append(LINE_FEED);
        writer.append(jsonBody).append(LINE_FEED);
        writer.flush();
    }

    /**
     * Adds a upload file section to the request
     *
     * @param uploadFile a File to be uploaded
     * @throws IOException
     */
    public void addFilePart(File uploadFile) {
        String fileName = uploadFile.getName();
        writer.append("--").append(boundary).append(LINE_FEED);
        writer.append("Content-Disposition: attachment;");
        writer.append(" filename=\"" + fileName + "\".").append(LINE_FEED);
        writer.append("Content-Type: image/*").append(LINE_FEED);
        writer.append(LINE_FEED);
        writer.flush();

        try {
            FileInputStream inputStream = new FileInputStream(uploadFile);
            //byte[] buffer = new byte[(int) uploadFile.length()];
            byte[] buffer = Files.readAllBytes(Paths.get(uploadFile.getPath()));
            int bytesRead = -1;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            outputStream.flush();
            inputStream.close();
        } catch (IOException ex) {
            logger.error("File transformation to bytes went wrong: {}", ex);
        }

        writer.append(LINE_FEED);
        writer.flush();
    }

    /**
     * Adds a header field to the request.
     *
     * @param name  - name of the header field
     * @param value - value of the header field
     */
    public void addHeaderField(String name, String value) {
        writer.append(name + ": " + value).append(LINE_FEED);
        writer.flush();
    }

    /**
     * Completes the request and receives response from the server.
     *
     * @return a list of Strings as response in case the server returned
     * status OK, otherwise an exception is thrown.
     * @throws IOException
     */
    public String finish() {
        String response = "";
        int status = 0;

        writer.flush();
        writer.append("--").append(boundary).append("--").append(LINE_FEED);
        writer.println();
        writer.close();

        try {
            // checks server's status code first
            status = httpConn.getResponseCode();

            if (status == HttpURLConnection.HTTP_OK) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(httpConn.getInputStream()));

                String line;
                while ((line = reader.readLine()) != null) {
                    response += line;
                }

                reader.close();
                httpConn.disconnect();
            } else {
                logger.error("OCR API returned error stream: {}", printErrorStream());
                throw new IOException("Server returned non-OK status: " + status + " : " + httpConn.getResponseMessage());
                //logger.error("Server returned non-OK status: " + status + " : " + httpConn.getResponseMessage());
            }
        } catch(IOException ex) {
            logger.error("Response message in Multipart finish has been received with problems: ", ex);
        }

        return response;
    }

    private String printErrorStream() throws IOException {
        //System.out.print("DEBUG System out ocr API error stream: ");
        InputStream errorStream = httpConn.getErrorStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(errorStream));
        String errLine = "", tempLine;
        while ((tempLine = reader.readLine()) != null) {
            errLine += tempLine;
        }

        return errLine;
    }

    private void getRequestHeaders(HttpURLConnection httpURLConnection) {
        for (Map.Entry<String, List<String>> entries : httpURLConnection.getRequestProperties().entrySet()) {
            String values = "";
            for (String value : entries.getValue()) {
                values += value + ",";
            }
            System.out.println("Request" + " " + entries.getKey() + " - " +  values );
        }
    }
}