Файл POST (в виде двоичного потока) с использованием java.net.HttpURLConnection в качестве файла параметров = <двоичный поток файла> - PullRequest
3 голосов
/ 27 июня 2019

Я пытаюсь загрузить (POST) файл в конечную точку, используя java.net.HttpURLConnection, но получаю http-код 400 (неверный запрос).

Я ссылался на Отправка файла и параметров на сервер с HttpURLConnection в Android API 23

но проблема в том, что мне нужно отправить этот файл в качестве параметра тела запроса (file =).

Примечание. Файлы будут иметь небольшой размер (4-5 МБ), поэтому я полностью читаю их в памяти.

Соответствующий запрос curl:

curl -X POST "API" -H "Тип контента: multipart / form-data" -F "file ="


Выдержки из кода, который я использую:

    Proxy webproxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(" 
                                <proxy host>", <proxy_port>));
    HttpURLConnection http_conn = (HttpURLConnection) 
                                     url.openConnection(webproxy);
    String authorization = getAuthorization(access_token);
    http_conn.setRequestMethod("POST");
    http_conn.setRequestProperty("Accept-Charset", "UTF-8");        
    http_conn.setRequestProperty("Authorization", authorization);
    http_conn.setRequestProperty("Connection", "Keep-Alive");
    http_conn.setRequestProperty("Content-Type", "multipart/form-data);  
    http_conn.setDoOutput(true);
    http_conn.setDoInput(true);
    DataOutputStream outputStream;
    outputStream = new DataOutputStream(http_conn.getOutputStream());
    File file_obj = new File(this.file);                                
    byte[] allBytes = new byte[(int) file_obj.length()];
    FileInputStream fileInputStream = new FileInputStream(file_obj);                
    outputStream.write("file=".getBytes("UTF-8")); <---Trying to add file param here
    fileInputStream.read(allBytes);
    outputStream.write(allBytes);

Пост, что я только что прочитал ответ, используя приведенный ниже фрагмент кода (отлично работает для разных запросов GET):

    InputStream inputStream = http_conn.getInputStream();            
        BufferedReader bufferedReader = new BufferedReader(new 
    InputStreamReader(inputStream));
    String line = "";            
    while ((line = bufferedReader.readLine()) != null) {
        data = data + line;                
    }

Примечание: я редко использую java, и я не очень хорошо с ней знаком, поэтому, пожалуйста, дайте пояснения в своем ответе.

Ответы [ 2 ]

4 голосов
/ 27 июня 2019

Если посмотреть на вашу командную строку curl, она показывает, что файл необходимо отправить как запрос multipart/form-data. На самом деле это сложный способ форматирования данных, когда это требуется.

Пример формата, который вам нужно отправить:

Заголовки:

Content-Type: multipart/form-data; boundary=AaB03x

Body:

--AaB03x
Content-Disposition: form-data; name="files"; filename="file1.txt"
Content-Type: text/plain

... contents of file1.txt ...
--AaB03x--

В данный момент ваш код отправляет файл в виде запроса в формате POST / GET, и это не работает, поскольку серверная часть этого не ожидает.

Чтобы решить эту проблему, нам нужно отформатировать исходные файлы в формат, требуемый бэкэндом, и как только вы узнаете, что опция заголовка «border» является просто случайно сгенерированным значением, отправлять запрос становится проще.

String boundary = "MY_AWESOME_BOUNDARY"
http_conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);  

try(DataOutputStream outputStream = new DataOutputStream(http_conn.getOutputStream())) {
    File file_obj = new File(this.file);                      

    // Write form-data header   
    outputStream.write(("--" + boundary + "\r\n").getBytes("UTF-8"));
    outputStream.write(("Content-Disposition: form-data; name=\"file\"; filename=\"file1.txt\"\r\n").getBytes("UTF-8"));
    outputStream.write(("Content-Type: text/plain\r\n").getBytes("UTF-8"));
    outputStream.write(("\r\n").getBytes("UTF-8"));
    // Write form-data body
    Files.copy(file_obj.toPath(), outputStream)
    // Write form-data "end"
    outputStream.write(("--" + boundary + "--\r\n").getBytes("UTF-8"));
}
// Read backend response here
try(InputStream inputStream = http_conn.getInputStream()) {           
    BufferedReader bufferedReader = new BufferedReader(new 
    InputStreamReader(inputStream));
    StringBuilder lines = new StringBuilder(); // StringBuilder is faster for concatination than appending strings           
    while ((line = bufferedReader.readLine()) != null) {
        lines.append(line);                
    }
    System.out.println(lines);
}

Обратите внимание, что я использовал блоки "try-with-resource", эти блоки гарантируют, что любые внешние ресурсы закрываются и удаляются, когда вы их используете, обычно лимит открытых ресурсов ОС очень низок по сравнению с объем памяти, который имеет ваша программа, так что ваша программа может выдавать странные ошибки, которые происходят только после некоторого времени работы или когда пользователь выполняет определенные действия внутри вашего приложения

1 голос
/ 28 июня 2019

Вышеуказанное не сработало для меня, поэтому я перешел на другой пакет (okhttp3), вот что у меня сработало:

    File file_obj = new File(this.file); 
    String authorization = "my authorization string";
    Proxy webproxy = new Proxy(Proxy.Type.HTTP, new 
          InetSocketAddress("proxy", <port>));

    OkHttpClient client = new OkHttpClient.Builder().proxy(webproxy).build();      

    RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart("file", "filename",
        RequestBody.create(MediaType.parse("application/octet-stream"), file_obj)).build();

    Request request = new Request.Builder().header("Authorization", authorization).url(this.url).post(requestBody).build();

    try (Response response = client.newCall(request).execute()){
        if(!response.isSuccessful()) return "NA";
        return (response.body().string());
    }
...