安卓:Retrofit 2如何实现多文件上传?

10

使用Retrofit 2上传单个图像似乎没有问题。

然而,我无法弄清如何同时上传两张图片。

如果按照文档操作:http://square.github.io/retrofit/2.x/retrofit/retrofit2/http/PartMap.html

File file = new File(path, "theimage");
File file2 = new File(path2, "theimage");
RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);
RequestBody requestBody2 = RequestBody.create(MediaType.parse("image/png"), file2);
Map<String, RequestBody> params = new HashMap<>();
params.put("image2", requestBody2 );

Call<ResponseBody> call = service.upload(requestBody, params);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Response<ResponseBody> response, Retrofit retrofit) {
    Log.v("Upload", "success");
}

接口:

public interface FileUploadService {

    @Multipart
    @POST("/upload")
    Call<ResponseBody> upload(
        //@Part("image_logo\"; filename=\"image.png\" ") RequestBody file,
        @Part("file") RequestBody file,
        @PartMap Map<String, RequestBody> params
     //  @Part("description") String description
);

这段代码返回了“上传:成功”,但在服务器端却收到了乱码:

CONTENT_TYPE: multipart/form-data; boundary=50fbfeb3-3abc-4f15-b130-cdcb7e3a0e4f

CONTENT POST:Array ( [file] => �PNG IHDR L 一大堆二进制乱码.... ... 省略 [file2] => �PNG IHDR L 更多二进制乱码...

请问有谁能指导我正确的方向吗?

单文件上传可以正常工作,问题应该不在那里,我现在尝试上传两个或更多图片。


如果我将代码改成这样:

HashMap<String, RequestBody> partMap = new HashMap<String, RequestBody>();
partMap.put("file\"; filename=\"" + file.getName(), requestBody);
partMap.put("file\"; filename=\"" + file2.getName(), requestBody);
Call<ResponseBody> call = service.upload(partMap);

@Multipart
@POST("/upload")
Call<ResponseBody> upload(
    @PartMap() Map<String, RequestBody> partMap,

我只能看到第二张图片被上传了,没有乱码... !?


更新

我尝试了这个Retrofit(2.0 beta2) Multipart文件上传不起作用的解决方法,但是收到一个错误提示@body不能与multipart一起使用: Java.lang.IllegalArgumentException: @Body参数不能与表单或多部分编码一起使用。(参数#1)

        for (String key : keys) {
            Bitmap bm = selectedImages.get(key);
            File f = new File(saveToInternalStorage(bm, key), key);
            if (f.exists()) {
                buildernew.addFormDataPart(key, key + ".png", RequestBody.create(MEDIA_TYPE, f));
            }
        }
        RequestBody requestBody = buildernew.build();
Call<ResponseBody> upload(
    @Body RequestBody requestBody

你找到解决方案了吗? - Vincent Paing
3个回答

7

这是可行的:

            final MediaType MEDIA_TYPE=MediaType.parse("image/png");
            HashMap<String,RequestBody> map=new HashMap<>(selectedImages.size());
            RequestBody file=null;
            File f=null;
            Set<String> keys = selectedImages.keySet();
            for (String key : keys) {
                try {
                    Bitmap bitmap = selectedImages.get(key);
                    f = new File(saveToInternalStorage(bitmap, key), key);

                    FileOutputStream fos = new FileOutputStream(f);
                    if(bitmap!=null){
                        bitmap.compress(Bitmap.CompressFormat.PNG, 0 , fos);
                        fos.flush();
                        fos.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    return;
                }

                file=RequestBody.create(MEDIA_TYPE, f);
                map.put(""+key+"\"; filename=\""+key+".jpg",file);
                Log.i("##MYLOG###", "### MAP PUT:" + key + " filename:"+key+".jpg file:" + file.toString() +" type:"+ file.contentType() );
                file=null;
                f = null;
            }

--

Call<ResponseBody> upload(
        @PartMap() Map<String,RequestBody> mapFileAndName //for sending multiple images

--

警告:在使用httpClient.interceptors()调试时,我只看到了单个上传,但当检查端点本身以查看它实际收到的内容时,它确实收到了多个上传!


selectedImages 是什么?我猜是一个 HashMap - Raptor
1
请问您可以告诉我在哪里放置服务器密钥吗? - Kaushik

2

我可能有点晚了,但我的回答可能会对未来的访问者有所帮助。

我要求用户像这样选择多个图像:

int PICK_IMAGE_MULTIPLE = 1;    
Intent intent = new Intent();
       intent.setType("image/*");
       intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
       intent.setAction(Intent.ACTION_GET_CONTENT);
       startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_IMAGE_MULTIPLE);

然后在 onActivityResult() 中,我正在执行以下操作:

ArrayList<String> filePaths;

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == PICK_IMAGE_MULTIPLE) {
        if (data != null) {
            filePaths=new ArrayList<>();
            // If data.getData() == null means multiple images selected, else single image selected.
            if (data.getData() == null) {
                ClipData clipData = data.getClipData();
                if (clipData != null) {
                    for (int i = 0; i < clipData.getItemCount(); i++) {
                        ClipData.Item item = clipData.getItemAt(i);
                        Uri uri = item.getUri();
                        filePaths.add(FileUtils.getPath(Activity.this, uri));
                    }
                }
            } else {
                filePaths.add(FileUtils.getPath(Activity.this,data.getData()));
            }
            sendToServer();
        }
    }
}

你可以从这个Github链接获取FileUtils类。
我的sendToServer()方法如下:
private void sendToServer() {
    if(filePaths!=null) {
        ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
        MediaType MEDIA_TYPE_IMG = MediaType.parse("image/jpeg");
        MultipartBody.Builder builder=new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);
        RequestBody requestBody;
        try {
            for (int i = 0; i < filePaths.size(); i++) {
                File file = new File(filePaths.get(i));
                requestBody=RequestBody.create(MEDIA_TYPE_IMG,file);
                builder.addFormDataPart("photo"+i,file.getName(),requestBody);
            }
            RequestBody finalRequestBody=builder.build();
            Call<YourResponse> call=apiService.addEvent(finalRequestBody);
            call.enqueue(new Callback<YourResponse>() {
                @Override
                public void onResponse(Call<YourResponse> call, Response<YourResponse> response) {
                    // process response
                }

                @Override
                public void onFailure(Call<YourResponse> call, Throwable t) {
                    t.printStackTrace();
                    t.getCause();
                }
            });
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

最终我的Retrofit端点看起来像这样:

最后,我的Retrofit端点看起来像这样:

@POST("event/add")
Call<YourResponse> addEvent(@Body RequestBody body);

请注意,YourResponse 可以是您自定义的处理响应的模型类,或者您也可以使用原始的 Response 类,如果您不想创建自己的模型类。
希望这能帮助新访客。

1
嗨,我尝试了这个方法,但对于使用retrofit2.0的多部分请求没有起作用。 - Bhavikkumar
@bhavikkumar 如果你正确使用它,它会起作用的。你还需要在服务器上进行适当的实现。请在Stack Overflow上提出另一个问题,说明你的问题和所有代码。在你发布问题后给我链接。 - rusted brain

1

尝试这个

对于API:

//Multiple Images
@Multipart
@POST(HttpConstants.FILEMULTIPLEUPLOAD)
Call<Result>uploadMultipleImage(@Part MultipartBody.Part files1,@Part MultipartBody.Part files2, @Query("total_images") int total, @Query("stdID") int stdID);

客户端

    public class RaytaServiceClass {
        public RaytaServiceClass() {
        }

        private static Retrofit getRetroClient(){
            Gson gson = new GsonBuilder()
                    .setLenient()
                    .create();
            return new Retrofit.Builder()
                    .baseUrl(HttpConstants.baseUrl)
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .build();
        }

        public static RaytaApi getApiService(){
            return getRetroClient().create(RaytaApi.class);
        }
    }

电话

     RaytaApi service= RaytaServiceClass.getApiService();

            File file1 = new File(selectedPath1);
            File file2 = new File(selectedPath2);

            RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file1);
            RequestBody requestFile2 = RequestBody.create(MediaType.parse("multipart/form-data"), file2);


            MultipartBody.Part body =
                    MultipartBody.Part.createFormData("uploaded_file", file1.getName(), requestFile);

            MultipartBody.Part body2 =
                    MultipartBody.Part.createFormData("uploaded_file", file2.getName(), requestFile2);


            Call<Result> resultCall=service.uploadMultipleImage(body,body2,2,1);
            Log.v("@@@@WWE","REquest "+resultCall.toString());
            Log.v("@@@WWE","Retrofit Request Method =  "+resultCall.request().method());
            Log.v("@@@WWE","Retrofit Request Body =  "+resultCall.request().body());
            Log.v("@@@WWE","Retrofit Request Url = "+resultCall.request().url());
            final Result[] result = {new Result()};

            resultCall.enqueue(new Callback<Result>() {
                @Override
                public void onResponse(Call<Result> call, Response<Result> response) {
                    progressDialog.dismiss();
                    Log.v("@@@WWE","Respnse");
                    result[0] =response.body();
                    Log.v("@@@WWE","Response Result "+result[0].getResult());
                    if(response.isSuccessful()){
                        Toast.makeText(UploadMultipleImageActivity.this,"Sucess",Toast.LENGTH_SHORT).show();
                        Toast.makeText(UploadMultipleImageActivity.this,"Press Refresh Button",Toast.LENGTH_LONG).show();
                        supportFinishAfterTransition();
                    }
                }

                @Override
                public void onFailure(Call<Result> call, Throwable t) {
                    progressDialog.dismiss();
                    Log.v("@@@WWE","Failure ");
                    Log.v("@@@WWE","MEssage "+t.getMessage());
                }
            });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接