Отправка файлов с помощью POST с HttpURLConnection

Поскольку разработчики Android рекомендуют использовать класс HttpURLConnection, Мне было интересно, может ли кто-нибудь предоставить мне хороший пример того, как отправить растровый «файл» (фактически поток в памяти) через POST на HTTP-сервер Apache. Меня не интересуют файлы cookie, аутентификация или что-то сложное, но я просто хочу иметь надежную и логичную реализацию. Все примеры, которые я видел здесь, больше похожи на «давайте попробуем это, и, может быть, это сработает».

Прямо сейчас у меня есть этот код:

URL url;
HttpURLConnection urlConnection = null;
try {
    url = new URL("http://example.com/server.cgi");

    urlConnection = (HttpURLConnection) url.openConnection();

} catch (Exception e) {
    this.showDialog(getApplicationContext(), e.getMessage());
}
finally {
    if (urlConnection != null)
    {
        urlConnection.disconnect();
    }
}

где showDialog должен просто отображать AlertDialog (в случае недопустимого URL?).

Теперь предположим, что я генерирую растровое изображение следующим образом: Bitmap image = this.getBitmap() внутри элемента управления, производного от View, и я хочу отправить его через POST. Какой была бы правильная процедура для достижения такого результата? Какие классы мне нужно использовать? Могу ли я использовать HttpPost, как в этом примере? Если да, то как мне построить InputStreamEntity для моего растрового изображения? Мне было бы противно потребовать сначала сохранить растровое изображение в файле на устройстве.


Я также должен упомянуть, что мне действительно нужно отправить на сервер каждый неизмененный пиксель исходного растрового изображения, поэтому я не могу преобразовать его в JPEG.


person Mihai Todor    schedule 01.08.2012    source источник
comment
Обратитесь к этому обсуждению SO   -  person Aniket Thakur    schedule 12.04.2017


Ответы (10)


Я понятия не имею, почему класс HttpURLConnection не предоставляет никаких средств для отправки файлов без необходимости вручную составлять оболочку файла. Вот что я в итоге сделал, но если кто-то знает лучшее решение, дайте мне знать.

Входные данные:

Bitmap bitmap = myView.getBitmap();

Статический материал:

String attachmentName = "bitmap";
String attachmentFileName = "bitmap.bmp";
String crlf = "\r\n";
String twoHyphens = "--";
String boundary =  "*****";

Настройте запрос:

HttpURLConnection httpUrlConnection = null;
URL url = new URL("http://example.com/server.cgi");
httpUrlConnection = (HttpURLConnection) url.openConnection();
httpUrlConnection.setUseCaches(false);
httpUrlConnection.setDoOutput(true);

httpUrlConnection.setRequestMethod("POST");
httpUrlConnection.setRequestProperty("Connection", "Keep-Alive");
httpUrlConnection.setRequestProperty("Cache-Control", "no-cache");
httpUrlConnection.setRequestProperty(
    "Content-Type", "multipart/form-data;boundary=" + this.boundary);

Запустить оболочку содержимого:

DataOutputStream request = new DataOutputStream(
    httpUrlConnection.getOutputStream());

request.writeBytes(this.twoHyphens + this.boundary + this.crlf);
request.writeBytes("Content-Disposition: form-data; name=\"" +
    this.attachmentName + "\";filename=\"" + 
    this.attachmentFileName + "\"" + this.crlf);
request.writeBytes(this.crlf);

Преобразовать Bitmap в ByteBuffer:

//I want to send only 8 bit black & white bitmaps
byte[] pixels = new byte[bitmap.getWidth() * bitmap.getHeight()];
for (int i = 0; i < bitmap.getWidth(); ++i) {
    for (int j = 0; j < bitmap.getHeight(); ++j) {
        //we're interested only in the MSB of the first byte, 
        //since the other 3 bytes are identical for B&W images
        pixels[i + j] = (byte) ((bitmap.getPixel(i, j) & 0x80) >> 7);
    }
}

request.write(pixels);

Конечная оболочка содержимого:

request.writeBytes(this.crlf);
request.writeBytes(this.twoHyphens + this.boundary + 
    this.twoHyphens + this.crlf);

Очистить выходной буфер:

request.flush();
request.close();

Получите ответ:

InputStream responseStream = new 
    BufferedInputStream(httpUrlConnection.getInputStream());

BufferedReader responseStreamReader = 
    new BufferedReader(new InputStreamReader(responseStream));

String line = "";
StringBuilder stringBuilder = new StringBuilder();

while ((line = responseStreamReader.readLine()) != null) {
    stringBuilder.append(line).append("\n");
}
responseStreamReader.close();

String response = stringBuilder.toString();

Закрыть поток ответов:

responseStream.close();

Закройте соединение:

httpUrlConnection.disconnect();

PS: Конечно, мне пришлось заключить запрос в private class AsyncUploadBitmaps extends AsyncTask<Bitmap, Void, String>, чтобы сделать платформу Android счастливой, потому что ей не нравятся сетевые запросы в основном потоке.

person Mihai Todor    schedule 06.08.2012
comment
Наконец-то прекрасно объясненный ответ на этот вопрос! Спасибо! Кстати, я только что нашел эту статью из блога разработчиков Android (android -developers.blogspot.com/2011/09/), где предлагается использовать HTTPURLConnection через Apache HTTPClient. Ваше здоровье! - person Andrés Pachon; 18.01.2013
comment
PS: Согласно this, `[error] =› 3` означает, что загруженный файл был загружен только частично, поэтому я могу предположить, что есть некоторые проблемы с буферизацией, но я понятия не имею, как отлаживать / исправлять что-то подобное. - person Mihai Todor; 20.08.2014
comment
См. Мой комментарий чуть выше. Вам нужно добавить их в переменную url, например: URL url = new URL("http://example.com/?param1=val1&param2=val2");. Вы можете добавлять сколько угодно (хотя я думаю, что есть некоторые ограничения). - person Mihai Todor; 12.03.2016
comment
Отлично, просто упускает одну вещь: этот responseStreamReader должен быть закрыт в конце try / catch. Таким образом: попробуйте {ВСЕ ВАШИ КОДЫ} catch (IOException e) {e.printStackTrace (); } наконец {если (соединение! = null) connection.disconnect (); попробуйте {если (responseStreamReader! = null) responseStreamReader.close (); } catch (IOException e) {e.printStackTrace (); }} - person FlorianB; 23.11.2016
comment
Отличный ответ! Предлагаю взглянуть на эту документацию w3.org о формах - person pippo; 11.09.2017

Я действительно нашел лучший способ отправлять файлы с помощью HttpURLConnection, используя MultipartEntity.

private static String multipost(String urlString, MultipartEntity reqEntity) {
    try {
        URL url = new URL(urlString);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setReadTimeout(10000);
        conn.setConnectTimeout(15000);
        conn.setRequestMethod("POST");
        conn.setUseCaches(false);
        conn.setDoInput(true);
        conn.setDoOutput(true);

        conn.setRequestProperty("Connection", "Keep-Alive");
        conn.addRequestProperty("Content-length", reqEntity.getContentLength()+"");
        conn.addRequestProperty(reqEntity.getContentType().getName(), reqEntity.getContentType().getValue());

        OutputStream os = conn.getOutputStream();
        reqEntity.writeTo(conn.getOutputStream());
        os.close();
        conn.connect();

        if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
            return readStream(conn.getInputStream());
        }

    } catch (Exception e) {
        Log.e(TAG, "multipart post error " + e + "(" + urlString + ")");
    }
    return null;        
}

private static String readStream(InputStream in) {
    BufferedReader reader = null;
    StringBuilder builder = new StringBuilder();
    try {
        reader = new BufferedReader(new InputStreamReader(in));
        String line = "";
        while ((line = reader.readLine()) != null) {
            builder.append(line);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return builder.toString();
} 

Предполагая, что вы загружаете изображение с растровыми данными:

    Bitmap bitmap = ...;
    String filename = "filename.png";
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
    ContentBody contentPart = new ByteArrayBody(bos.toByteArray(), filename);

    MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
    reqEntity.addPart("picture", contentPart);
    String response = multipost("http://server.com", reqEntity);

И вуаля! Данные вашего сообщения будут содержать поле изображения вместе с именем файла и путем на вашем сервере.

person Pencilcheck    schedule 12.05.2013
comment
Я заметил, что вы не вызываете conn.disconnect (), это намеренно? - person jerrytouille; 14.05.2013
comment
@MihaiTodor. Вам нужно сжать растровое изображение в файл, чтобы в любом случае уменьшить объем данных, передаваемых по сети. - person stealthcopter; 07.11.2013
comment
@stealthcopter Ну, это зависит от разработчика, но мой метод позволяет людям сохранять исходные пиксели растрового изображения. В моем случае (растровые изображения 256x256 для демонстрации) сжатие растрового изображения не стоило хлопот, но для реального приложения определенно будет хорошей практикой использовать некоторый механизм архивирования. - person Mihai Todor; 08.11.2013
comment
вам следует вызвать setFixedLengthStreamingMode (reqEntity.getContentLength ()) вместо того, чтобы напрямую устанавливать заголовок Content-Length. Таким образом, ваши данные не буферизуются перед отправкой в ​​сокет (по крайней мере, на более новых устройствах, на Android 2.3 или менее кажется, что они все равно буферизуются). Я исправил проблему с буферизацией на старых устройствах, используя okhttp в качестве транспорта, и это работает. - person Matt Wolfe; 06.12.2013
comment
очень плохо MultipartEntity не входит в комплект Android SDK - person mente; 06.12.2013
comment
@mente Он связан с httpmime. Я использую это как зависимость Gradle: org.apache.httpcomponents: httpmime: 4.1.1 - person david.perez; 11.07.2014
comment
Работает нормально, но мне пришлось прокомментировать conn.addRequestProperty("Content-length", reqEntity.getContentLength()+""), потому что он генерировал искаженный запрос с дублированным заголовком Content-length: со значением -1. Apache отвечал 400 Bad Request еще до завершения загрузки. Длина содержимого устанавливается автоматически HttpURLConnection. - person Giorgio Ruffa; 03.02.2016
comment
Это его часть android теперь как com.android.internal.http.multipart.MultipartEntity, поэтому зависимость gradle не требуется. - person Marc; 18.05.2017

Просто загрузить файл на сервер с каким-либо параметром с помощью MultipartUtility.

MultipartUtility.java

public class MultipartUtility {

    private final String boundary;
    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)
            throws IOException {
        this.charset = charset;

        // creates a unique boundary based on time stamp
        boundary = "===" + System.currentTimeMillis() + "===";

        URL url = new URL(requestURL);
        Log.e("URL", "URL : " + requestURL.toString());
        httpConn = (HttpURLConnection) url.openConnection();
        httpConn.setUseCaches(false);
        httpConn.setDoOutput(true); // indicates POST method
        httpConn.setDoInput(true);
        httpConn.setRequestProperty("Content-Type",
                "multipart/form-data; boundary=" + boundary);
        httpConn.setRequestProperty("User-Agent", "CodeJava Agent");
        httpConn.setRequestProperty("Test", "Bonjour");
        outputStream = httpConn.getOutputStream();
        writer = new PrintWriter(new OutputStreamWriter(outputStream, charset),
                true);
    }

    /**
     * Adds a form field to the request
     *
     * @param name  field name
     * @param value field value
     */
    public void addFormField(String name, String value) {
        writer.append("--" + boundary).append(LINE_FEED);
        writer.append("Content-Disposition: form-data; name=\"" + name + "\"")
                .append(LINE_FEED);
        writer.append("Content-Type: text/plain; charset=" + charset).append(
                LINE_FEED);
        writer.append(LINE_FEED);
        writer.append(value).append(LINE_FEED);
        writer.flush();
    }

    /**
     * Adds a upload file section to the request
     *
     * @param fieldName  name attribute in <input type="file" name="..." />
     * @param uploadFile a File to be uploaded
     * @throws IOException
     */
    public void addFilePart(String fieldName, File uploadFile)
            throws IOException {
        String fileName = uploadFile.getName();
        writer.append("--" + boundary).append(LINE_FEED);
        writer.append(
                "Content-Disposition: form-data; name=\"" + fieldName
                        + "\"; filename=\"" + fileName + "\"")
                .append(LINE_FEED);
        writer.append(
                "Content-Type: "
                        + URLConnection.guessContentTypeFromName(fileName))
                .append(LINE_FEED);
        writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
        writer.append(LINE_FEED);
        writer.flush();

        FileInputStream inputStream = new FileInputStream(uploadFile);
        byte[] buffer = new byte[4096];
        int bytesRead = -1;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }
        outputStream.flush();
        inputStream.close();

        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() throws IOException {
        StringBuffer response = new StringBuffer();

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

        // checks server's status code first
        int status = httpConn.getResponseCode();
        if (status == HttpURLConnection.HTTP_OK) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    httpConn.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            reader.close();
            httpConn.disconnect();
        } else {
            throw new IOException("Server returned non-OK status: " + status);
        }

        return response.toString();
    }
}

К upload вы file вместе с параметрами.

ПРИМЕЧАНИЕ: поместите этот код ниже в поток, отличный от пользовательского интерфейса, чтобы получить ответ.

String charset = "UTF-8";
String requestURL = "YOUR_URL";

MultipartUtility multipart = new MultipartUtility(requestURL, charset);
multipart.addFormField("param_name_1", "param_value");
multipart.addFormField("param_name_2", "param_value");
multipart.addFormField("param_name_3", "param_value");
multipart.addFilePart("file_param_1", new File(file_path));
String response = multipart.finish(); // response from server.
person Jaydipsinh Zala    schedule 15.10.2015
comment
спасибо @Jay, ты только что спас мой проект и мою жизнь. : D - person gumuruh; 18.11.2020

Решение Jaydipsinh Zala не сработало для меня, я не знаю почему, но, похоже, оно близко к решению.

Итак, объединив это с отличным решением и объяснением Михая Тодора, в результате получился класс, который в настоящее время работает для меня. Если это кому-то поможет:

MultipartUtility2V.java

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;

public class MultipartUtilityV2 {
    private HttpURLConnection httpConn;
    private DataOutputStream request;
    private final String boundary =  "*****";
    private final String crlf = "\r\n";
    private final String twoHyphens = "--";

    /**
     * This constructor initializes a new HTTP POST request with content type
     * is set to multipart/form-data
     *
     * @param requestURL
     * @throws IOException
     */
    public MultipartUtilityV2(String requestURL)
            throws IOException {

        // creates a unique boundary based on time stamp
        URL url = new URL(requestURL);
        httpConn = (HttpURLConnection) url.openConnection();
        httpConn.setUseCaches(false);
        httpConn.setDoOutput(true); // indicates POST method
        httpConn.setDoInput(true);

        httpConn.setRequestMethod("POST");
        httpConn.setRequestProperty("Connection", "Keep-Alive");
        httpConn.setRequestProperty("Cache-Control", "no-cache");
        httpConn.setRequestProperty(
                "Content-Type", "multipart/form-data;boundary=" + this.boundary);

        request =  new DataOutputStream(httpConn.getOutputStream());
    }

    /**
     * Adds a form field to the request
     *
     * @param name  field name
     * @param value field value
     */
    public void addFormField(String name, String value)throws IOException  {
        request.writeBytes(this.twoHyphens + this.boundary + this.crlf);
        request.writeBytes("Content-Disposition: form-data; name=\"" + name + "\""+ this.crlf);
        request.writeBytes("Content-Type: text/plain; charset=UTF-8" + this.crlf);
        request.writeBytes(this.crlf);
        request.writeBytes(value+ this.crlf);
        request.flush();
    }

    /**
     * Adds a upload file section to the request
     *
     * @param fieldName  name attribute in <input type="file" name="..." />
     * @param uploadFile a File to be uploaded
     * @throws IOException
     */
    public void addFilePart(String fieldName, File uploadFile)
            throws IOException {
        String fileName = uploadFile.getName();
        request.writeBytes(this.twoHyphens + this.boundary + this.crlf);
        request.writeBytes("Content-Disposition: form-data; name=\"" +
                fieldName + "\";filename=\"" +
                fileName + "\"" + this.crlf);
        request.writeBytes(this.crlf);

        byte[] bytes = Files.readAllBytes(uploadFile.toPath());
        request.write(bytes);
    }

    /**
     * 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() throws IOException {
        String response ="";

        request.writeBytes(this.crlf);
        request.writeBytes(this.twoHyphens + this.boundary +
                this.twoHyphens + this.crlf);

        request.flush();
        request.close();

        // checks server's status code first
        int status = httpConn.getResponseCode();
        if (status == HttpURLConnection.HTTP_OK) {
            InputStream responseStream = new
                    BufferedInputStream(httpConn.getInputStream());

            BufferedReader responseStreamReader =
                    new BufferedReader(new InputStreamReader(responseStream));

            String line = "";
            StringBuilder stringBuilder = new StringBuilder();

            while ((line = responseStreamReader.readLine()) != null) {
                stringBuilder.append(line).append("\n");
            }
            responseStreamReader.close();

            response = stringBuilder.toString();
            httpConn.disconnect();
        } else {
            throw new IOException("Server returned non-OK status: " + status);
        }

        return response;
    }
}
person Georgevik    schedule 10.03.2016
comment
Это решение работает только у меня. Большое Вам спасибо. - person Yucel Bayram; 26.12.2018
comment
но почему вы не сбрасываете в конце addFilePart ()? Я видел, что вы выполнили промывку в конце addFormField (). @Georgevik - person gumuruh; 18.11.2020

Этот ответ https://stackoverflow.com/a/33149413/6481542 помог мне на 90% загружать большие файлы. на сервер Django разработки, но мне пришлось использовать setFixedLengthStreamingMode, чтобы это сработало. Это требует установки Content-Length перед записью содержимого, что требует довольно значительной переписывания приведенного выше ответа. Вот мой конечный результат

public class MultipartLargeUtility {
    private final String boundary;
    private static final String LINE_FEED = "\r\n";
    private HttpURLConnection httpConn;
    private String charset;
    private OutputStream outputStream;
    private PrintWriter writer;
    private final int maxBufferSize = 4096;
    private long contentLength = 0;
    private URL url;

    private List<FormField> fields;
    private List<FilePart> files;

    private class FormField {
        public String name;
        public String value;

        public FormField(String name, String value) {
            this.name = name;
            this.value = value;
        }
    }

    private class FilePart {
        public String fieldName;
        public File uploadFile;

        public FilePart(String fieldName, File uploadFile) {
            this.fieldName = fieldName;
            this.uploadFile = uploadFile;
        }
    }

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

        // creates a unique boundary based on time stamp
        boundary = "===" + System.currentTimeMillis() + "===";
        url = new URL(requestURL);
        fields = new ArrayList<>();
        files = new ArrayList<>();

        if (requireCSRF) {
            getCSRF();
        }
    }

    /**
     * Adds a form field to the request
     *
     * @param name  field name
     * @param value field value
     */
    public void addFormField(String name, String value)
            throws UnsupportedEncodingException {
        String fieldContent = "--" + boundary + LINE_FEED;
        fieldContent += "Content-Disposition: form-data; name=\"" + name + "\"" + LINE_FEED;
        fieldContent += "Content-Type: text/plain; charset=" + charset + LINE_FEED;
        fieldContent += LINE_FEED;
        fieldContent += value + LINE_FEED;
        contentLength += fieldContent.getBytes(charset).length;
        fields.add(new FormField(name, value));
    }

    /**
     * Adds a upload file section to the request
     *
     * @param fieldName  name attribute in <input type="file" name="..." />
     * @param uploadFile a File to be uploaded
     * @throws IOException
     */
    public void addFilePart(String fieldName, File uploadFile)
            throws IOException {
        String fileName = uploadFile.getName();

        String fieldContent = "--" + boundary + LINE_FEED;
        fieldContent += "Content-Disposition: form-data; name=\"" + fieldName
                + "\"; filename=\"" + fileName + "\"" + LINE_FEED;
        fieldContent += "Content-Type: "
                + URLConnection.guessContentTypeFromName(fileName) + LINE_FEED;
        fieldContent += "Content-Transfer-Encoding: binary" + LINE_FEED;
        fieldContent += LINE_FEED;
        // file content would go here
        fieldContent += LINE_FEED;
        contentLength += fieldContent.getBytes(charset).length;
        contentLength += uploadFile.length();
        files.add(new FilePart(fieldName, uploadFile));
    }

    /**
     * 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 List<String> finish() throws IOException {
        List<String> response = new ArrayList<String>();
        String content = "--" + boundary + "--" + LINE_FEED;
        contentLength += content.getBytes(charset).length;

        if (!openConnection()) {
            return response;
        }

        writeContent();

        // checks server's status code first
        int status = httpConn.getResponseCode();
        if (status == HttpURLConnection.HTTP_OK) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    httpConn.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) {
                response.add(line);
            }
            reader.close();
            httpConn.disconnect();
        } else {
            throw new IOException("Server returned non-OK status: " + status);
        }
        return response;
    }

    private boolean getCSRF()
            throws IOException {
        /// First, need to get CSRF token from server
        /// Use GET request to get the token
        CookieManager cookieManager = new CookieManager();
        CookieHandler.setDefault(cookieManager);
        HttpURLConnection conn = null;

        conn = (HttpURLConnection) url.openConnection();

        conn.setUseCaches(false); // Don't use a Cached Copy
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Connection", "Keep-Alive");
        conn.getContent();
        conn.disconnect();

        /// parse the returned object for the CSRF token
        CookieStore cookieJar = cookieManager.getCookieStore();
        List<HttpCookie> cookies = cookieJar.getCookies();
        String csrf = null;
        for (HttpCookie cookie : cookies) {
            Log.d("cookie", "" + cookie);
            if (cookie.getName().equals("csrftoken")) {
                csrf = cookie.getValue();
                break;
            }
        }
        if (csrf == null) {
            Log.d(TAG, "Unable to get CSRF");
            return false;
        }
        Log.d(TAG, "Received cookie: " + csrf);

        addFormField("csrfmiddlewaretoken", csrf);
        return true;
    }

    private boolean openConnection()
            throws IOException {
        httpConn = (HttpURLConnection) url.openConnection();
        httpConn.setUseCaches(false);
        httpConn.setDoOutput(true);    // indicates POST method
        httpConn.setDoInput(true);
        //httpConn.setRequestProperty("Accept-Encoding", "identity");
        httpConn.setFixedLengthStreamingMode(contentLength);
        httpConn.setRequestProperty("Connection", "Keep-Alive");
        httpConn.setRequestProperty("Content-Type",
                "multipart/form-data; boundary=" + boundary);
        outputStream = new BufferedOutputStream(httpConn.getOutputStream());
        writer = new PrintWriter(new OutputStreamWriter(outputStream, charset),
                true);
        return true;
    }

    private void writeContent()
            throws IOException {

        for (FormField field : fields) {
            writer.append("--" + boundary).append(LINE_FEED);
            writer.append("Content-Disposition: form-data; name=\"" + field.name + "\"")
                    .append(LINE_FEED);
            writer.append("Content-Type: text/plain; charset=" + charset).append(
                    LINE_FEED);
            writer.append(LINE_FEED);
            writer.append(field.value).append(LINE_FEED);
            writer.flush();
        }

        for (FilePart filePart : files) {
            String fileName = filePart.uploadFile.getName();
            writer.append("--" + boundary).append(LINE_FEED);
            writer.append(
                    "Content-Disposition: form-data; name=\"" + filePart.fieldName
                            + "\"; filename=\"" + fileName + "\"")
                    .append(LINE_FEED);
            writer.append(
                    "Content-Type: "
                            + URLConnection.guessContentTypeFromName(fileName))
                    .append(LINE_FEED);
            writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
            writer.append(LINE_FEED);
            writer.flush();

            FileInputStream inputStream = new FileInputStream(filePart.uploadFile);
            int bufferSize = Math.min(inputStream.available(), maxBufferSize);
            byte[] buffer = new byte[bufferSize];
            int bytesRead = -1;
            while ((bytesRead = inputStream.read(buffer, 0, bufferSize)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            outputStream.flush();
            inputStream.close();
            writer.append(LINE_FEED);
            writer.flush();
        }

        writer.append("--" + boundary + "--").append(LINE_FEED);
        writer.close();
    }
}

Использование во многом такое же, как в приведенном выше ответе, но я включил поддержку CSRF, которую Django использует по умолчанию с формами.

boolean useCSRF = true;
MultipartLargeUtility multipart = new MultipartLargeUtility(url, "UTF-8",useCSRF);
multipart.addFormField("param1","value");
multipart.addFilePart("filefield",new File("/path/to/file"));
List<String> response = multipart.finish();
Log.w(TAG,"SERVER REPLIED:");
for(String line : response) {
    Log.w(TAG, "Upload Files Response:::" + line);
}
person Nick C    schedule 18.06.2016
comment
Привет, Ник, как мне прочитать опубликованный файл на принимающей стороне? Я хочу прочитать файл и записать на диск. - person user1447718; 03.06.2021

на основе решения Михая, если у кого-то есть проблема с сохранением изображений на сервере, как это произошло на моем сервере. измените Bitmap на часть байтового буфера на:

ByteArrayOutputStream bos = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG,100,bos);
        byte[] pixels = bos.toByteArray();
person m_____ilk    schedule 27.06.2016

Я не тестировал это, но вы можете попробовать использовать PipedInputStream и PipedOutputStream. Это может выглядеть примерно так:

final Bitmap bmp = … // your bitmap

// Set up Piped streams
final PipedOutputStream pos = new PipedOutputStream(new ByteArrayOutputStream());
final PipedInputStream pis = new PipedInputStream(pos);

// Send bitmap data to the PipedOutputStream in a separate thread
new Thread() {
    public void run() {
        bmp.compress(Bitmap.CompressFormat.PNG, 100, pos);
    }
}.start();

// Send POST request
try {
    // Construct InputStreamEntity that feeds off of the PipedInputStream
    InputStreamEntity reqEntity = new InputStreamEntity(pis, -1);

    HttpClient httpclient = new DefaultHttpClient();
    HttpPost httppost = new HttpPost(url);
    reqEntity.setContentType("binary/octet-stream");
    reqEntity.setChunked(true);
    httppost.setEntity(reqEntity);
    HttpResponse response = httpclient.execute(httppost);
} catch (Exception e) {
    e.printStackTrace()
}
person Rico Yao    schedule 02.08.2012

Вот что я сделал для загрузки фотографии с помощью почтового запроса.

public void uploadFile(int directoryID, String filePath) {
    Bitmap bitmapOrg = BitmapFactory.decodeFile(filePath);
    ByteArrayOutputStream bao = new ByteArrayOutputStream();

    String upload_url = BASE_URL + UPLOAD_FILE;
    bitmapOrg.compress(Bitmap.CompressFormat.JPEG, 90, bao);

    byte[] data = bao.toByteArray();

    HttpClient httpClient = new DefaultHttpClient();
    HttpPost postRequest = new HttpPost(upload_url);
    MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);

    try {
        // Set Data and Content-type header for the image
        FileBody fb = new FileBody(new File(filePath), "image/jpeg");
        StringBody contentString = new StringBody(directoryID + "");

        entity.addPart("file", fb);
        entity.addPart("directory_id", contentString);
        postRequest.setEntity(entity);

        HttpResponse response = httpClient.execute(postRequest);
        // Read the response
        String jsonString = EntityUtils.toString(response.getEntity());
        Log.e("response after uploading file ", jsonString);

    } catch (Exception e) {
        Log.e("Error in uploadFile", e.getMessage());
    }
}

ПРИМЕЧАНИЕ. Для этого кода требуются библиотеки, поэтому следуйте инструкциям здесь, чтобы получить библиотеки.

person Sheraz Ahmad Khilji    schedule 26.03.2014
comment
Приятно знать, что есть способ использовать последнюю версию HttpClient (ваша ссылка устарела. Используйте вместо этого), который разработчики Android сохраняют только для обратной совместимости, а не используют встроенный HttpURLConnection. С другой стороны, похоже, что для его настройки требуется больше работы, так что, возможно, это не стоит усилий. - person Mihai Todor; 26.03.2014

Мне стало намного проще использовать okHttp, так как я не мог заставить работать ни одно из этих решений: https://stackoverflow.com/a/37942387/447549

person Clive Jefferies    schedule 21.06.2016