【问题标题】:How to upload an image file in Retrofit 2如何在 Retrofit 2 中上传图像文件
【发布时间】:2017-02-18 14:10:39
【问题描述】:

我有一张邮递员的图片,如下所示。如何在 Retrofit 2 中做同样的事情?

我已经这样声明了接口:

@Multipart
@POST("/api/Pharmarcy/UploadImage")
Call<ResponseBody> uploadPrescriptionImage(
        @Query("accessToken") String token,
        @Query("pharmarcyRequestId") int pharmacyRequestedId,
        @Part MultipartBody.Part image);

【问题讨论】:

  • 那么问题是什么?

标签: android retrofit2


【解决方案1】:
@Multipart
@POST("user/updateprofile")
Observable<ResponseBody> updateProfile(@Part("user_id") RequestBody id,
                                       @Part("full_name") RequestBody fullName,
                                       @Part MultipartBody.Part image,
                                       @Part("other") RequestBody other);

//pass it like this
File file = new File("/storage/emulated/0/Download/Corrections 6.jpg");
RequestBody requestFile =
        RequestBody.create(MediaType.parse("multipart/form-data"), file);

// MultipartBody.Part is used to send also the actual file name
MultipartBody.Part body =
        MultipartBody.Part.createFormData("image", file.getName(), requestFile);

// add another part within the multipart request
RequestBody fullName = 
        RequestBody.create(MediaType.parse("multipart/form-data"), "Your Name");

service.updateProfile(id, fullName, body, other);

看看我传递多部分和字符串参数的方式。希望对您有所帮助!

【讨论】:

  • 你能解释一下什么是“user_id”、“full_name”和“other”吗?是否可以根据问题参数更新您的答案?
  • @Ultimo_m 这些是 API 请求参数。
  • 那天我遇到了一个问题(最终与这个帖子无关),我在看邮递员照片,body中唯一的参数是image,同时你还有“RequestBody fullName”我想也许有我不知道的东西。无论如何感谢您的重播:)
  • @android_griezmann 我只需要使用 multipart 将图像传递给 api,该怎么做?
  • @AviPatel 我不熟悉传递 POJO 类,但我猜如果请求的内容类型是“Application/JSON”,那么你可以传递 POJO。
【解决方案2】:

对于有 inputStream 的用户,您可以使用 Multipart 上传 inputStream。

@Multipart
@POST("pictures")
suspend fun uploadPicture(
        @Part part: MultipartBody.Part
): NetworkPicture

然后在你的存储库类中:

suspend fun upload(inputStream: InputStream) {
   val part = MultipartBody.Part.createFormData(
         "pic", "myPic", RequestBody.create(
              MediaType.parse("image/*"),
              inputStream.readBytes()
          )
   )
   uploadPicture(part)
}

如果您的后端不允许多部分,您可以将输入流转换为字节并将字节数组作为请求正文发送,如下所示。

// In your service
 @PUT
 suspend fun s3Upload(
     @Header("Content-Type") mime: String,
     @Url uploadUrl: String, 
     @Body body: RequestBody 
 )
// In your repository
val body = RequestBody.create(MediaType.parse("application/octet"), inputStream.readBytes())
networkService.s3Upload(mime, url, body)

要获得输入流,您可以这样做。

在您的片段或活动中,您需要创建一个返回InputStream 的图像选择器。 InputStream 的优势在于它可以用于云上的文件,例如 google drive 和 dropbox。

通过View.OnClickListeneronOptionsItemSelected 致电pickImagesLauncher.launch("image/*")。 (见Activity Result APIs)。

private val pickImagesLauncher =
           registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
                uri?.let {
                    val stream = contentResolver.openInputStream(it)
                    itemViewModel.uploadPicture(stream)
                }
            }

override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)

      btn.setOnClickListener {
         pickImagesLauncher.launch("image/*")
     }
 }

【讨论】:

  • 如何从imageUri: Uri创建InputStream
  • @binrebin 我已完成更新答案以包括创建 InputStream
  • 如果我们执行readBytes而不提及大小,那是不是会将整个文件内容加载到内存中,并导致OutOfMemory异常?
  • 不推荐使用像 onActivityResult 这样的 API。最好使用名为 ActivityResultContractregisterForActivityResult() 的新 API。
  • @Mohsents 我在仅 Compose 的应用程序中尝试了 registerForActivityResult,但我不推荐它。你最终会得到一些尴尬的代码。如果您打算做一个仅 Compose 的应用程序,请坚持使用 onActivityResult。
【解决方案3】:

上传图片看这里点击这个链接

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

class AppConfig {

    private static String BASE_URL = "http://mushtaq.16mb.com/";

    static Retrofit getRetrofit() {

        return new Retrofit.Builder()
                .baseUrl(AppConfig.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
}

========================================================
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;

interface ApiConfig {
    @Multipart
    @POST("retrofit_example/upload_image.php")
    Call<ServerResponse> uploadFile(@Part MultipartBody.Part file,
                                    @Part("file") RequestBody name);

    /*@Multipart
    @POST("ImageUpload")
    Call<ServerResponseKeshav> uploadFile(@Part MultipartBody.Part file,
                                    @Part("file") RequestBody name);*/

    @Multipart
    @POST("retrofit_example/upload_multiple_files.php")
    Call<ServerResponse> uploadMulFile(@Part MultipartBody.Part file1,
                                       @Part MultipartBody.Part file2);
}

//https://drive.google.com/open?id=0BzBKpZ4nzNzUMnJfaklVVTJkWEk

【讨论】:

  • 能否请您分享请求参数的密钥我在邮递员中收到此错误
    警告:move_uploaded_file(): Unable to move '/tmp/phpbhv7zZ' to ' uploads/device-2018-07-24-132846.png' 在 /home/u511106771/public_html/retrofit_example/upload_image.php12
    {"成功":false,"message":"上传时出错"}
【解决方案4】:

我完全同意@tir38 和@android_griezmann。这将是 Kotlin 中的版本:

interface servicesEndPoint {
@Multipart
@POST("user/updateprofile")
fun updateProfile(@Part("user_id") id:RequestBody, @Part("full_name") fullName:RequestBody, @Part image: MultipartBody.Part, @Part("other") other:RequestBody): Single<UploadPhotoResponse>

companion object {
        val API_BASE_URL = "YOUR_URL"

        fun create(): servicesPhotoEndPoint {
            val retrofit = Retrofit.Builder()
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(API_BASE_URL)
                .build()
            return retrofit.create(servicesPhotoEndPoint::class.java)
        }
    }
}

// Pass it like this
val file = File(RealPathUtils.getRealPathFromURI_API19(context, uri))
val requestFile: RequestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file)

// MultipartBody.Part is used to send also the actual file name
val body: MultipartBody.Part = MultipartBody.Part.createFormData("image", file.name, requestFile)

// Add another part within the multipart request
val fullName: RequestBody = RequestBody.create(MediaType.parse("multipart/form-data"), "Your Name")

servicesEndPoint.create().updateProfile(id, fullName, body, fullName)

要获取真实路径,请使用 RealPathUtils。在这个问题的@Harsh Bhavsar 的答案中检查这个类:How to get the Full file path from URI

getRealPathFromURI_API19,您需要READ_EXTERNAL_STORAGE的权限。

【讨论】:

    【解决方案5】:

    使用 Retrofit 2.0 你可以这样:

    @Multipart
        @POST("uploadImage")
        Call<ResponseBody> uploadImage(@Part("file\"; fileName=\"myFile.png\" ")RequestBody requestBodyFile, @Part("image") RequestBody requestBodyJson);
    

    提出请求:

    File imgFile = new File("YOUR IMAGE FILE PATH");
    RequestBody requestBodyFile = RequestBody.create(MediaType.parse("image/*"), imgFile);
    RequestBody requestBodyJson = RequestBody.create(MediaType.parse("text/plain"),
                        retrofitClient.getJsonObject(uploadRequest));
    
    
    
    //make sync call
    Call<ResponseBody> uploadBundle = uploadImpl.uploadImage(requestBodyFile, requestBodyJson);
    Response<BaseResponse> response = uploadBundle.execute();
    

    请参考https://square.github.io/retrofit/

    【讨论】:

    • 如何传递动态名称?
    【解决方案6】:
    @Multipart
    @POST(Config.UPLOAD_IMAGE)
    Observable<Response<String>> uploadPhoto(@Header("Access-Token") String header, @Part MultipartBody.Part imageFile);
    

    你可以这样调用这个api:

       public void uploadImage(File file) {
         // create multipart
         RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
        MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile);
    
        // upload
        getViewInteractor().showProfileUploadingProgress();
    
        Observable<Response<String>> observable = api.uploadPhoto("",body);
    
        // on Response
        subscribeForNetwork(observable, new ApiObserver<Response<String>>() {
            @Override
            public void onError(Throwable e) {
                getViewInteractor().hideProfileUploadingProgress();
            }
    
            @Override
            public void onResponse(Response<String> response) {
    
                if (response.code() != 200) {
                    Timber.d("error " + response.code());
                    return;
                }
                getViewInteractor().hideProfileUploadingProgress();
                getViewInteractor().onProfileImageUploadSuccess(response.body());
    
            }
        });
    
    }
    

    【讨论】:

    • 在 subscribeForNetwork 等回复后我不明白(...你能解释一下吗
    • @HamzaRahman 抱歉,这是我的自定义方法,来自 rxjava2 new CompositeDisposable().add(observable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableObserver () { public void onNext(Void aVoid) { } public void onError(Throwable e) { } })); }
    • 好的,我明白了,谢谢@iamdeowanshi
    • 很高兴为您提供帮助
    【解决方案7】:

    改造2.0解决方案

    @Multipart
    @POST(APIUtils.UPDATE_PROFILE_IMAGE_URL)
    public Call<CommonResponse> requestUpdateImage(@PartMap Map<String, RequestBody> map);
    

        Map<String, RequestBody> params = new HashMap<>();
    
        params.put("newProfilePicture" + "\"; filename=\"" + FilenameUtils.getName(file.getAbsolutePath()), RequestBody.create(MediaType.parse("image/jpg"), file));
    
    
    
     Call<CommonResponse> call = request.requestUpdateImage(params);
    

    你可以使用
    图片/jpg 图片/png 图片/gif

    【讨论】:

    • 我想发送姓名电子邮件以及图像,我该怎么做?
    【解决方案8】:

    这很容易。这是API接口

    public interface Api {
    
        @Multipart
        @POST("upload")
        Call<MyResponse> uploadImage(@Part("image\"; filename=\"myfile.jpg\" ") RequestBody file, @Part("desc") RequestBody desc);
    
    }
    

    您可以使用以下代码拨打电话。

    private void uploadFile(File file, String desc) {
    
            //creating request body for file
            RequestBody requestFile = RequestBody.create(MediaType.parse(getContentResolver().getType(fileUri)), file);
            RequestBody descBody = RequestBody.create(MediaType.parse("text/plain"), desc);
    
            //The gson builder
            Gson gson = new GsonBuilder()
                    .setLenient()
                    .create();
    
    
            //creating retrofit object
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(Api.BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .build();
    
            //creating our api 
            Api api = retrofit.create(Api.class);
    
            //creating a call and calling the upload image method 
            Call<MyResponse> call = api.uploadImage(requestFile, descBody);
    
            //finally performing the call 
            call.enqueue(new Callback<MyResponse>() {
                @Override
                public void onResponse(Call<MyResponse> call, Response<MyResponse> response) {
                    if (!response.body().error) {
                        Toast.makeText(getApplicationContext(), "File Uploaded Successfully...", Toast.LENGTH_LONG).show();
                    } else {
                        Toast.makeText(getApplicationContext(), "Some error occurred...", Toast.LENGTH_LONG).show();
                    }
                }
    
                @Override
                public void onFailure(Call<MyResponse> call, Throwable t) {
                    Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show();
                }
            });
        }
    

    来源:Retrofit Upload File Tutorial

    【讨论】:

      猜你喜欢
      • 2016-08-31
      • 2021-08-28
      • 2018-04-11
      • 1970-01-01
      • 2018-07-04
      • 2016-09-04
      • 2017-05-14
      • 1970-01-01
      相关资源
      最近更新 更多