44

I use okhttp to be my httpclient. I think it's a good api but the doc is not so detailed.

how to use it to make a http post request with file uploading?

public Multipart createMultiPart(File file){
    Part part = (Part) new Part.Builder().contentType("").body(new File("1.png")).build();
    //how to  set part name?
    Multipart m = new Multipart.Builder().addPart(part).build();
    return m;
}
public String postWithFiles(String url,Multipart m) throws  IOException{
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    m.writeBodyTo(out)
    ;
    Request.Body body =  Request.Body.create(MediaType.parse("application/x-www-form-urlencoded"),
            out.toByteArray());

    Request req = new Request.Builder().url(url).post(body).build();
    return client.newCall(req).execute().body().string();

}

my question is:

  1. how to set part name? in the form, the file should be named file1.
  2. how to add other fields in the form?
ento
  • 5,613
  • 5
  • 49
  • 68
user2219372
  • 2,015
  • 3
  • 19
  • 24
  • do you have any success with it ? i want to upload multiple files – AZ_ May 19 '14 at 07:49
  • not yet..i search for some days ,and doesn't get any result. – user2219372 May 19 '14 at 12:25
  • I am using different approach because Volley is not designed for uploading. I have watched Google I/O. try using Loopj – AZ_ May 20 '14 at 01:29
  • I had noticed this lib.It seems that can only run in android vm? The benefit of okhttp is running in Sun's Java vm.It's easy to do unit test.Could you run loopj out of android VM? – user2219372 May 20 '14 at 08:25
  • what error do you get? – njzk2 May 20 '14 at 17:32
  • Yes i can run in Android VM – AZ_ May 21 '14 at 03:45
  • Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/http/params/HttpParams if i add the org.apache.httpcomponents to the pom.xm,it would says errors like :android .io.XXX error,I had tried it for a few times – user2219372 May 23 '14 at 14:21

6 Answers6

50

Here is a basic function that uses okhttp to upload a file and some arbitrary field (it literally simulates a regular HTML form submission)

Change the mime type to match your file (here I am assuming .csv) or make it a parameter to the function if you are going to upload different file types

  public static Boolean uploadFile(String serverURL, File file) {
    try {

        RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
                .addFormDataPart("file", file.getName(),
                        RequestBody.create(MediaType.parse("text/csv"), file))
                .addFormDataPart("some-field", "some-value")
                .build();

        Request request = new Request.Builder()
                .url(serverURL)
                .post(requestBody)
                .build();

        client.newCall(request).enqueue(new Callback() {

            @Override
            public void onFailure(final Call call, final IOException e) {
                // Handle the error
            }

            @Override
            public void onResponse(final Call call, final Response response) throws IOException {
                if (!response.isSuccessful()) {
                    // Handle the error
                }
                // Upload successful
            }
        });

        return true;
    } catch (Exception ex) {
        // Handle the error
    }
    return false;
}

Note: because it is async call, the boolean return type does not indicate successful upload but only that the request was submitted to okhttp queue.

Christian Vielma
  • 12,606
  • 12
  • 52
  • 59
iTech
  • 17,211
  • 4
  • 52
  • 78
  • Does it add Content-Length header automatically? – zygimantus Feb 03 '16 at 20:29
  • 10
    Note: For unresolved class names, check this [link](http://stackoverflow.com/a/34676385/2304737) – mr5 Feb 04 '16 at 05:50
  • I've spent two days trying to upload a file from Android to an Asp.Net MVC HttpPost action method. The other, more basic, java solutions made the connection but failed with the file upload - and were almost impossible to debug. This was the solution that finally worked! – Stephen Hosking Dec 14 '16 at 20:17
  • **BUT**, when I upgraded okhttp to 3.5.0 it no longer compiled. See comment above by mr5 for resolving links, and answer below by @Bryant Kou for changed class names. – Stephen Hosking Dec 14 '16 at 20:49
  • where you use `text/csv` What will i use for mp3 file or jpg ? is there any random or auto method which work on all type of files ? – Ahmad Oct 05 '19 at 09:13
42

Here's an answer that works with OkHttp 3.2.0:

public void upload(String url, File file) throws IOException {
    OkHttpClient client = new OkHttpClient();
    RequestBody formBody = new MultipartBody.Builder()
        .setType(MultipartBody.FORM)
        .addFormDataPart("file", file.getName(),
            RequestBody.create(MediaType.parse("text/plain"), file))
        .addFormDataPart("other_field", "other_field_value")
        .build();
    Request request = new Request.Builder().url(url).post(formBody).build();
    Response response = client.newCall(request).execute();
}
Mike Yang
  • 1,864
  • 1
  • 19
  • 25
Bryant Kou
  • 1,360
  • 15
  • 16
22

Note: this answer is for okhttp 1.x/2.x. For 3.x, see this other answer.

The class Multipart from mimecraft encapsulates the whole HTTP body and can handle regular fields like so:

Multipart m = new Multipart.Builder()
        .type(Multipart.Type.FORM)
        .addPart(new Part.Builder()
                .body("value")
                .contentDisposition("form-data; name=\"non_file_field\"")
                .build())
        .addPart(new Part.Builder()
                .contentType("text/csv")
                .body(aFile)
                .contentDisposition("form-data; name=\"file_field\"; filename=\"file1\"")
                .build())
        .build();

Take a look at examples of multipart/form-data encoding to get a sense of how you need to construct the parts.

Once you have a Multipart object, all that's left to do is specify the right Content-Type header and pass on the body bytes to the request.

Since you seem to be working with the v2.0 of the OkHttp API, which I don't have experience with, this is just guess code:

// You'll probably need to change the MediaType to use the Content-Type
// from the multipart object
Request.Body body =  Request.Body.create(
        MediaType.parse(m.getHeaders().get("Content-Type")),
        out.toByteArray());

For OkHttp 1.5.4, here is a stripped down code I'm using which is adapted from a sample snippet:

OkHttpClient client = new OkHttpClient();
OutputStream out = null;
try {
    URL url = new URL("http://www.example.com");
    HttpURLConnection connection = client.open(url);
    for (Map.Entry<String, String> entry : multipart.getHeaders().entrySet()) {
        connection.addRequestProperty(entry.getKey(), entry.getValue());
    }
    connection.setRequestMethod("POST");
    // Write the request.
    out = connection.getOutputStream();
    multipart.writeBodyTo(out);
    out.close();

    // Read the response.
    if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
        throw new IOException("Unexpected HTTP response: "
                + connection.getResponseCode() + " " + connection.getResponseMessage());
    }
} finally {
    // Clean up.
    try {
        if (out != null) out.close();
    } catch (Exception e) {
    }
}
Community
  • 1
  • 1
ento
  • 5,613
  • 5
  • 49
  • 68
  • It worked by changing " Request.Body body = Request.Body.create(MediaType.parse("application/x-www-form-urlencoded"), out.toByteArray());" to "Request.Body.create(MediaType.parse(m.getHeaders().get("Content-Type"))" – user2219372 May 23 '14 at 13:46
  • Thanks for the follow-up! – ento May 24 '14 at 03:39
  • what dependency should I add for `Multipart`? –  Jan 24 '16 at 02:34
  • 2
    @jawanam If you're using OkHttp 3.x, then an equivalent class is already included in OkHttp. See [the other answer](http://stackoverflow.com/a/30498514/20226) for its usage. The `Mulitpart` in this answer refers to [mimecraft](https://github.com/square/mimecraft)'s. – ento Jan 24 '16 at 06:42
  • this answer is to old – Serg Burlaka Apr 02 '19 at 19:38
3

I've created cool helper class for OkHttp3. it here

public class OkHttp3Helper {

    public static final String TAG;
    private static final okhttp3.OkHttpClient client;

    static {
        TAG = OkHttp3Helper.class.getSimpleName();
        client = new okhttp3.OkHttpClient.Builder()
                .readTimeout(7, TimeUnit.MINUTES)
                .writeTimeout(7, TimeUnit.MINUTES)
                .build();
    }

    private Context context;

    public OkHttp3Helper(Context context) {
        this.context = context;
    }

    /**
     * <strong>Uses:</strong><br/>
     * <p>
     * {@code
     * ArrayMap<String, String> formField = new ArrayMap<>();}
     * <br/>
     * {@code formField.put("key1", "value1");}<br/>
     * {@code formField.put("key2", "value2");}<br/>
     * {@code formField.put("key3", "value3");}<br/>
     * <br/>
     * {@code String response = helper.postToServer("http://www.example.com/", formField);}<br/>
     * </p>
     *
     * @param url       String
     * @param formField android.support.v4.util.ArrayMap
     * @return response from server in String format
     * @throws Exception
     */
    @NonNull
    public String postToServer(@NonNull String url, @Nullable ArrayMap<String, String> formField)
            throws Exception {
        okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder().url(url);
        if (formField != null) {
            okhttp3.FormBody.Builder formBodyBuilder = new okhttp3.FormBody.Builder();
            for (Map.Entry<String, String> entry : formField.entrySet()) {
                formBodyBuilder.add(entry.getKey(), entry.getValue());
            }
            requestBuilder.post(formBodyBuilder.build());
        }
        okhttp3.Request request = requestBuilder.build();
        okhttp3.Response response = client.newCall(request).execute();
        if (!response.isSuccessful()) {
            throw new IOException(response.message());
        }
        return response.body().string();
    }

    /**
     * <strong>Uses:</strong><br/>
     * <p>
     * {@code
     * ArrayMap<String, String> formField = new ArrayMap<>();}
     * <br/>
     * {@code formField.put("key1", "value1");}<br/>
     * {@code formField.put("key2", "value2");}<br/>
     * {@code formField.put("key3", "value3");}<br/>
     * <br/>
     * {@code
     * ArrayMap<String, File> filePart = new ArrayMap<>();}
     * <br/>
     * {@code filePart.put("key1", new File("pathname"));}<br/>
     * {@code filePart.put("key2", new File("pathname"));}<br/>
     * {@code filePart.put("key3", new File("pathname"));}<br/>
     * <br/>
     * {@code String response = helper.postToServer("http://www.example.com/", formField, filePart);}<br/>
     * </p>
     *
     * @param url       String
     * @param formField android.support.v4.util.ArrayMap
     * @param filePart  android.support.v4.util.ArrayMap
     * @return response from server in String format
     * @throws Exception
     */
    @NonNull
    public String postMultiPartToServer(@NonNull String url,
                                        @Nullable ArrayMap<String, String> formField,
                                        @Nullable ArrayMap<String, File> filePart)
            throws Exception {
        okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder().url(url);
        if (formField != null || filePart != null) {
            okhttp3.MultipartBody.Builder multipartBodyBuilder = new okhttp3.MultipartBody.Builder();
            multipartBodyBuilder.setType(okhttp3.MultipartBody.FORM);
            if (formField != null) {
                for (Map.Entry<String, String> entry : formField.entrySet()) {
                    multipartBodyBuilder.addFormDataPart(entry.getKey(), entry.getValue());
                }
            }
            if (filePart != null) {
                for (Map.Entry<String, File> entry : filePart.entrySet()) {
                    File file = entry.getValue();
                    multipartBodyBuilder.addFormDataPart(
                            entry.getKey(),
                            file.getName(),
                            okhttp3.RequestBody.create(getMediaType(file.toURI()), file)
                    );
                }
            }
            requestBuilder.post(multipartBodyBuilder.build());
        }
        okhttp3.Request request = requestBuilder.build();
        okhttp3.Response response = client.newCall(request).execute();
        if (!response.isSuccessful()) {
            throw new IOException(response.message());
        }
        return response.body().string();
    }

    private okhttp3.MediaType getMediaType(URI uri1) {
        Uri uri = Uri.parse(uri1.toString());
        String mimeType;
        if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
            ContentResolver cr = context.getContentResolver();
            mimeType = cr.getType(uri);
        } else {
            String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri
                    .toString());
            mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
                    fileExtension.toLowerCase());
        }
        return okhttp3.MediaType.parse(mimeType);
    }
}
Kasim Rangwala
  • 1,595
  • 2
  • 18
  • 40
3
OkHttpClient client = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).writeTimeout(180, TimeUnit.SECONDS).readTimeout(180, TimeUnit.SECONDS).build();
    RequestBody body = new MultipartBody.Builder().setType(MultipartBody.FORM)
            .addFormDataPart("File", path.getName(),RequestBody.create(MediaType.parse("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"),path))
            .addFormDataPart("username", username) 
            .addFormDataPart("password", password)
            .build();
    Request request = new Request.Builder().url(url).post(body).build();
    Response response = client.newCall(request).execute();
    result = response.body().string();

Above code will send the username, password as the post parameter and the file will be uploaded in the name of "File".

PHP Server will receive the files

    if (isset($_FILES["File"]) &&
        isset($_POST['username']) &&
        isset($_POST['password'])) {
        //All Values found
    }else{
        echo 'please send the required data';
    }
Vignesh KM
  • 1,420
  • 1
  • 13
  • 22
0

Perfect code for uploading any files to google drive along with metadata of files easily.

    String url = String.format("https://www.googleapis.com/upload/drive/v2/files?uploadType=multipart");

    //String url = String.format("https://www.googleapis.com/upload/drive/v2/files?uploadType=resumable");
    boolean status;
    String metaDataFile = "{\"title\":\"" + step.getFile_name() + "\"," +
            "\"description\":\"" + step.getDescription() + "\"," +
            "\"parents\":[{\"id\":\"" + step.getFolderId() + "\"}]," +
            "\"capabilities\":{\"canEdit\":\"" + false + "\", \"canDownload\":\" "+ false +" \" }, " +
            "\"type\":\"" + step.getFile_access() + "\"" +
            "}";

    //get the encoded byte data for decode
    byte[] file = Base64.decodeBase64(step.getFile_data());

    //attaching metadata to our request object
    RequestBody requestBodyMetaData = RequestBody.create(MediaType.parse("application/json"), metaDataFile);

    //passing both meta data and file content for uploading
    RequestBody requestBodyMultipart = new MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("metadata", null, requestBodyMetaData)
            .addFormDataPart("file", null, RequestBody.create(MediaType.parse("application/octet-stream"), file))
            .build();
    Request request = new Request.Builder()
            .url(url)
            .addHeader("Authorization", String.format("Bearer %s", step.getAccess_token()))
            .post(requestBodyMultipart)
            .build();

    OkHttpClient okHttpClient = new OkHttpClient();

    try {
        // Get response after rest call.
        Response response = okHttpClient.newCall(request).execute();
        status = response.code() == 200 ? true : false;
        map.put(step.getOutput_variable(), response.code());
nullptr
  • 1,870
  • 1
  • 17
  • 26
  • 1
    Welcome to Stack Overflow. While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value. [How to Answer](https://stackoverflow.com/help/how-to-answer) – Elletlar Jan 22 '19 at 10:33