Retrofit上传/下载文件 (一)

Retrofit是Square公司开源的简化 HTTP 请求的库,这篇文章主要介绍用Retrofit实现文件的上传与下载的功能.

本文使用的是Retrofit 2.0.2版本

1.文件上传

api service接口:

public interface UpLoadService {
    @Multipart
    @POST("upload.php")
    Call<ResponseBody> upload(@Part MultipartBody.Part file);
}

文件上传使用@Multipart注解,方法参数使用@Part

使用:

private void uploadFile(String filePath) {
    // create upload service client
    UpLoadService service =
            ServiceGenerator.createService(UpLoadService.class);

    // use the FileUtils to get the actual file by uri
    File file = new File(filePath);

    // create RequestBody instance from file
    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("file", file.getName(), requestFile);

    // finally, execute the request
    Call<ResponseBody> call = service.upload(body);
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call,
                               Response<ResponseBody> response) {
            try {
                Log.e("Upload", "success:"+response.body().string());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.e("Upload error:", t.getMessage());
        }
    });
}

使用RequestBody封装file,然后使用MultipartBody.Part组成表单数据进行提交,createFormData第一个参数file为表单的key,第二个参数为上传文件的文件名.

ResponseBody为okhttp3提供的请求返回数据封装类,可以根据服务器返回的数据使用converter转换成其他类型.比如使用GsonConverterFactory将返回数据转换为对应的实体类.

2.文件下载

api service接口:

public interface DownloadService {

    @GET
    Call<ResponseBody> download(@Url String fileUrl);

}

使用:

private void downloadFile(String fileUrl){
   DownloadService downloadService = ServiceGenerator.createService(DownloadService.class);

    Call<ResponseBody> call = downloadService.download(fileUrl);

    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            if (response.isSuccessful()) {
                Log.e("Download", "server contacted and has file");

                boolean writtenToDisk = writeResponseBodyToDisk(response.body(), "1.mp4");

                Log.e("Download", "file download was a success? " + writtenToDisk);
            } else {
                Log.e("Download", "server contact failed");
            }
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.e("Download", "error :"+t.getMessage());
        }
    });
}

将文件保存到本地:

private boolean writeResponseBodyToDisk(ResponseBody body,String savaName) {
   try {
        // todo change the file location/name according to your needs
        File futureStudioIconFile = new File(getExternalFilesDir(null) + File.separator + savaName);

        InputStream inputStream = null;
        OutputStream outputStream = null;

        try {
            byte[] fileReader = new byte[4096];

            long fileSize = body.contentLength();
            long fileSizeDownloaded = 0;

            inputStream = body.byteStream();
            outputStream = new FileOutputStream(futureStudioIconFile);

            while (true) {
                int read = inputStream.read(fileReader);

                if (read == -1) {
                    break;
                }

                outputStream.write(fileReader, 0, read);

                fileSizeDownloaded += read;

                Log.w("saveFile", "file download: " + fileSizeDownloaded + " of " + fileSize);
            }

            outputStream.flush();

            return true;
        } catch (IOException e) {
            return false;
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }

            if (outputStream != null) {
                outputStream.close();
            }
        }
    } catch (IOException e) {
        return false;
    }
}

但是这里下载文件有一个问题,CallBack里onResponse回调时是文件以及下载成功了并且此时已经将文件内容全部加载到了内存中.所以这里就有问题了,如果文件小还好如果文件大的话那就特别的耗内存了甚至造成内存溢出问题.

解决办法:

public interface DownloadService {
    @Streaming
    @GET
    Call<ResponseBody> download(@Url String fileUrl);
}

在方法上加上@Streaming注解,加上这个注解以后就不会讲文件内容加载到内存中,而是在通过ResponseBody 去读取文件的时候才从网络文件去下载文件.

加上@Streaming运行发现会报错,android.os.NetworkOnMainThreadException错误很明显是在主线程访问了网络,因为CallBack的回调是在主线程中,加上@Streaming后保存文件相当于是从网络读取文件内容保存到本地,所以会出这个错(个人理解不知道有没有错).

修改如下:

private void downloadFile(final String fileUrl){
    final DownloadService downloadService = ServiceGenerator.createService(DownloadService.class);

   Call<ResponseBody> call = downloadService.download(fileUrl);

 call.enqueue(new Callback<ResponseBody>() {
      @Override
      public void onResponse(Call<ResponseBody> call, final Response<ResponseBody> response) {
          if (response.isSuccessful()) {
              Log.e("Download", "server contacted and has file");
              new AsyncTask<Void, Long, Void>() {
                  @Override
                  protected Void doInBackground(Void... voids) {
                      boolean writtenToDisk = writeResponseBodyToDisk(response.body(), "1.zip");
                      Log.e("Download", "file download was a success? " + writtenToDisk);
                      return null;
                  }

              }.execute();

          } else {
              Log.e("Download", "server contact failed");
          }
      }

      @Override
      public void onFailure(Call<ResponseBody> call, Throwable t) {
          Log.e("Download", "error :" + t.getMessage());
      }
  });
}

加上了AsyncTask让保存文件在后台去执行,当然了我这里只是为了测试所以用了AsyncTask,你也可以用其他的或者使用RxJava,只要让保存的过程在后台执行就行.这样就解决了下载文件占用内存的问题.

好了Retrofit上传下载文件功能就实现了,但是真正使用的时候可能会发现问题,因为我们上传下载的时候一般都会显示一个上传/下载的进度,但是在上面的代码里并没有提现出来,而且Retrofit好像也并没有提供相关的api.不要急Retrofit很灵活的,我们完全可以自己扩展这个功能,下篇文章将介绍扩展上传下载进度的监听功能.

转载:https://www.loongwind.com/archives/284.html

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Kotlin 中使用 Retrofit 进行文件上传,你可以按照以下步骤进行操作: 1. 首先,确保你已经在项目中添加了 Retrofit 和相应的依赖库。可以在 `build.gradle` 文件中添加以下依赖: ```kotlin implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0' ``` 2. 创建一个接口,定义文件上传的 API。例如,假设你要上传文件到 `/upload` 路径,可以创建一个名为 `FileUploadService` 的接口: ```kotlin import okhttp3.MultipartBody import okhttp3.RequestBody import retrofit2.Call import retrofit2.http.Multipart import retrofit2.http.POST import retrofit2.http.Part interface FileUploadService { @Multipart @POST("/upload") fun uploadFile(@Part file: MultipartBody.Part): Call<ResponseBody> } ``` 3. 创建 Retrofit 实例,并且使用该实例创建 `FileUploadService` 的实例: ```kotlin import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory val retrofit = Retrofit.Builder() .baseUrl("http://your-base-url.com") .client(createOkHttpClient()) .addConverterFactory(GsonConverterFactory.create()) .build() fun createOkHttpClient(): OkHttpClient { val logger = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY } return OkHttpClient.Builder() .addInterceptor(logger) .build() } val fileUploadService = retrofit.create(FileUploadService::class.java) ``` 4. 现在,你可以使用 `fileUploadService` 实例来上传文件。首先,创建一个 `File` 对象,表示要上传文件。然后,将文件转换为 `RequestBody` 对象,并使用 `MultipartBody.Part` 将其添加到请求中: ```kotlin import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.MultipartBody import java.io.File val file = File("path/to/your/file") val requestFile = file.asRequestBody("image/*".toMediaTypeOrNull()) val body = MultipartBody.Part.createFormData("file", file.name, requestFile) val call = fileUploadService.uploadFile(body) call.enqueue(object : Callback<ResponseBody> { override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) { // 文件上传成功处理 } override fun onFailure(call: Call<ResponseBody>, t: Throwable) { // 文件上传失败处理 } }) ``` 这样就完成了 Kotlin 中使用 Retrofit 进行文件上传的过程。记得替换实际的 base URL 和文件路径。希望对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值