如何使用okhttp上传文件?

Posted

技术标签:

【中文标题】如何使用okhttp上传文件?【英文标题】:how to use okhttp to upload a file? 【发布时间】:2014-06-24 02:53:18 【问题描述】:

我使用 okhttp 作为我的 httpclient。我认为这是一个很好的api,但文档不是那么详细。

如何使用它来发出http post请求并上传文件?

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();


我的问题是:

    如何设置零件名称?在表格中,该文件应命名为 file1。 如何在表单中添加其他字段?

【问题讨论】:

你有什么成功的吗?我想上传多个文件 还没有..我搜索了几天,没有得到任何结果。 我使用不同的方法,因为 Volley 不是为上传而设计的。我看过谷歌 I/O。尝试使用 Loopj 我注意到了这个lib。似乎只能在android vm中运行? okhttp 的好处是运行在 Sun 的 Java 虚拟机中。很容易做单元测试。你能在 android 虚拟机中运行 loopj 吗? 你得到什么错误? 【参考方案1】:

这是一个基本函数,它使用 okhttp 上传文件和一些任意字段(它实际上模拟了常规的 html 表单提交)

更改 mime 类型以匹配您的文件(这里我假设为 .csv),或者如果您要上传不同的文件类型,请将其作为函数的参数

  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;

注意:因为是异步调用,所以布尔返回类型表示上传成功,只是表示请求已提交到okhttp队列。

【讨论】:

是否自动添加 Content-Length 标头? 注意:对于未解析的类名,请查看link 我花了两天时间尝试将文件从 Android 上传到 Asp.Net MVC HttpPost 操作方法。其他更基本的 java 解决方案建立了连接,但文件上传失败 - 并且几乎无法调试。这是最终奏效的解决方案! 但是,当我将 okhttp 升级到 3.5.0 时,它不再编译。请参阅上面 mr5 的评论以解决链接问题,并通过@Bryant Kou 在下面回答更改的类名。 你在哪里使用text/csv mp3 文件或 jpg 文件我将使用什么?是否有任何适用于所有类型文件的随机或自动方法?【参考方案2】:

这是一个适用于 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();

【讨论】:

OkHttpClient client = new OkHttpClient();【参考方案3】:

注意:此答案适用于 okhttp 1.x/2.x。对于 3.x,请参阅 this other answer。

mimecraft 中的 Multipart 类封装了整个 HTTP 主体,可以像这样处理常规字段:

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();

查看examples of multipart/form-data encoding,了解您需要如何构建这些部件。

一旦有了Multipart 对象,剩下要做的就是指定正确的Content-Type 标头并将正文字节传递给请求。

由于您似乎正在使用我没有经验的 OkHttp API v2.0,这只是猜测代码:

// 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());

对于 OkHttp 1.5.4,这是我正在使用的精简代码,改编自 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) 
    

【讨论】:

它通过改变“ Request.Body body = Request.Body.create(MediaType.parse("application/x-www-form-urlencoded"), out.toByteArray());"到 "Request.Body.create(MediaType.parse(m.getHeaders().get("Content-Type"))" 感谢您的跟进! 我应该为Multipart添加什么依赖? @jawanam 如果您使用的是 OkHttp 3.x,那么 OkHttp 中已经包含一个等效类。有关其用法,请参阅the other answer。此答案中的Mulitpart 指的是mimecraft。 这个答案太老了【参考方案4】:

我为OkHttp3 创建了很酷的助手类。在这里

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);
    

【讨论】:

【参考方案5】:
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();

以上代码将用户名、密码作为post参数发送,文件将以“文件”的名称上传。

php 服务器将接收文件

    if (isset($_FILES["File"]) &&
        isset($_POST['username']) &&
        isset($_POST['password'])) 
        //All Values found
    else
        echo 'please send the required data';
    

【讨论】:

【参考方案6】:

完美的代码可以轻松地将任何文件连同文件元数据上传到谷歌驱动器。

    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());

【讨论】:

欢迎来到 Stack Overflow。虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。 How to Answer【参考方案7】:

异步 ​​...

public void UploadFileFromOkhttp(String filePath, String jwtToken)
String url = "https://api.baserow.io/api/user-files/upload-file/";
MultipartBody.Builder builder = new MultipartBody.Builder();
builder.setType(MultipartBody.FORM);
File file = new File(filePath);
builder.addFormDataPart("file" , file.getName() , RequestBody.create(MediaType.parse("image/*"), file));
RequestBody requestBody = builder.build();
Request request = new Request.Builder()
        .url(url)
        .addHeader("Authorization", "JWT "+ jwtToken)
        .post(requestBody)
        .build();
okHttpClient.newCall(request).enqueue(new okhttp3.Callback() 
  @Override
  public void onFailure(@NotNull Call call, @NotNull IOException e) 
    activity.runOnUiThread(new Runnable() 
      @Override
      public void run() 
        OnError(e.getMessage());
      
    );
  

  @Override
  public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException 
      final String responseData = response.body().string();
      activity.runOnUiThread(new Runnable() 
        @Override
        public void run() 
          OnSuccess(responseData);
        
      );
  
);

【讨论】:

以上是关于如何使用okhttp上传文件?的主要内容,如果未能解决你的问题,请参考以下文章

okhttp实现断点上传

Android必知必会-使用okhttp的PUT方式上传文件

Android通过OKHTTP上传文件不使用MultiPart

使用 OkHttp 或 Retrofit 将文件上传到 AWS S3 存储桶

okhttp3实现post方式上传文件加参数

实战:5分钟搞懂OkHttp断点上传