Retrofit上传/下载文件 (一)

2023-10-29

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Retrofit上传/下载文件 (一) 的相关文章

  • 带身份验证的 MediaPlayer RTSP 视频流

    我能够在未经授权的情况下从网络摄像机流式传输视频 但现在我需要在授权的情况下执行此操作 我发现很少有信息表明 Android 不支持 RTSP 身份验证 但我发现另一条信息表明 通过使用该方法添加标头 可以在 API 级别 14 中实现身份
  • RecyclerView每隔几个项目都是相同的 - 可扩展项目

    我的 recyclerView 有问题 我正在使用此布局来扩展 recyclerView 中的 cardView https github com AAkira ExpandableLayout https github com AAkir
  • Android 中的列表视图到 pdf

    我有一个自定义列表视图 我想从整个列表视图制作pdf 我参考了很多帖子并实现了下面的代码 该代码将我的列表视图转pdf 但问题是它不包含整个列表视图项目 pdf 中仅提供前几项 我的转换函数列表视图转pdf is fab setOnClic
  • 无法禁用 Firestore 中的离线数据

    从我的数据中删除数据后Firestore Database 这需要我的Android app一段时间后才意识到数据已被删除 我认为这是由于自动数据缓存而发生的 我的应用程序与离线使用无关 我想禁用此功能 我已将其添加到我的自定义中Appli
  • 垂直翻转 Android Canvas

    有没有一种简单的方法可以在 Android 中翻转画布 我似乎找不到任何可以让我垂直翻转它的东西 这样 y 轴上的零就是手机屏幕的底部而不是顶部 如果解决方案不是特别快也没关系 因为我没有对画布进行任何计算密集的操作 提前致谢 Try ca
  • Android 上 Java 库中的代码出现 NoClassDefFoundError

    我的用户经常遇到错误 应用程序在启动期间崩溃 当应该加载 MainActivity 时 VM 显然找不到该类 我不明白为什么 该应用程序的架构是 我的免费版和专业版都使用一个通用项目 不知道是否相关 请参阅下面的堆栈跟踪 有什么想法吗 ja
  • Android物理引擎[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 在研究了 3D 游戏编程之后 很快就明白了为什么物理引擎非常有用 Android 支持哪些物理引擎并且可以在 Android 上使用 关于
  • 如何将您的终端与 Android 模拟器连接

    我尝试导航到 android 工具文件夹并输入 adb shell 命令 但它似乎不起作用 我的终端似乎只能识别命令的 adb 部分 并给我一条错误消息 我究竟做错了什么 通过键入列出所有连接的设备adb devices 检查是否列出了任何
  • Android/三星 Galaxy S 模拟器

    有没有办法在三星银河模拟器或类似的东西上尝试我的项目 我的项目在 HTC Legend 上运行 但在该设备上崩溃了 我如何在 android eclipse 上设置三星 Galaxy s 我设置了 W800 854 2 2 AVD 但它可以
  • 如何更改蜂窝中儿童偏好屏幕的背景颜色

    过去几天我一直在寻找解决方案 但找不到 我需要更改右窗格的背景颜色 我知道如何更改左父首选项的颜色 我在清单文件中创建了一个新主题
  • 如何从Android webview下载文件?

    我下面的代码可以很好地加载 url 页面 并且在搜索歌曲后 当我单击下载链接时 它崩溃了 关于如何让下载管理器与网络视图一起工作的教程并不多 我究竟做错了什么 import java io File import android app A
  • 谷歌的Json解析Gson库:JsonElement和JsonObject有什么区别?

    public abstract class JsonElement extends Object 表示 Json 元素的类 它可以是 JsonObject JsonArray JsonPrimitive 或 JsonNull public
  • 应用程序运行时相对布局中的元素显示不同

    我有一个ListView在片段内创建 并且它有一个搜索过滤器 问题是 XML 布局在 android studio 中显示正常 但在模拟器或手机中运行时 它显示不同 与我对齐时不正确 并且当我单击SearchView它位于选项卡导航下方 谁
  • 特定铃声 firebase 通知 xamarin.android

    How i can force the push notification to run ringtone instead of default notification sound is there any way to ovveride
  • Android 报告“Error=Unable to find Instrumentation info for: ComponentInfo {}”

    stackoverflow 上有很多关于这个问题的主题 问题 例如错误 无法找到以下仪器信息 ComponentInfo https stackoverflow com q 21294945 513413 和其他网站 但使我的问题与其他网站
  • 安卓独立包

    我有一个很大的 UI 大约 20 25 个屏幕 我应该如何组织我的代码 我应该按功能分成不同的包吗 我是否应该为所有 UI 类创建一个包 然后创建子包进行组织 或者我不应该创建单独的包并组织到文件夹中 任何帮助将不胜感激 当您创建文件夹时
  • 如何在没有 OpenCv Manager 的情况下运行 OpenCV 代码

    我正在使用 OpenCV4Android 版本 2 4 10 并在 Samsung Galayx GT I9300 上测试我的代码 我遇到的问题是 我必须从 Play 商店下载 Opencv Manager 以便我的 opencv 代码运行
  • 如何根据受保护的 String doInBackground 方法中 AsyncTask 的结果调用 Toast.makeText() ?

    我从 AsyncTask 中的数据库中获取数据 如果它为空 我想吐司一个警告文本 我在 AsyncTask 中尝试过 但我了解到它不是在工作线程中调用的 这是我的 doInBackground 方法 protected String doI
  • 如何在不下载子项的情况下从 Firebase 获取子项密钥?

    我有一个 Firebase 数据库 其中的节点 items 有很多子项 我想导入子项键的列表 由于每个子项都包含相当多我对此不感兴趣的数据 因此我想仅下载子项密钥 以最大程度地减少传输的数据量 为了便于说明 假设我有以下数据结构 然后我想获
  • 活动构建变体没有测试工件

    我基于 调试 构建变体创建了一个名为 bitrise 的新构建类型 使用 debug 构建变体时 经过检测的 androidTests 构建并运行良好 但是当我切换到新的 bitrise 构建变体时 出现以下错误 Process finis

随机推荐

  • LRU Cache的数据结构选择以及实现

    LRU LRU是Least Recently Used的缩写 意思是最近最少使用 它是一种Cache替换算法 什么是Cache 狭义的Cache指的是位于CPU和主存间的快速RAM 通常它不像系统主存那样使用DRAM技术 而使用昂贵但较快速
  • STC 32位8051单片机开发实例教程 二 I/O工作模式及其配置

    1 I O工作模式 STC 32G系列单片机最多有64Pin引脚 最多有60个I O口 如下图示 STC32G系列单片机的 I O口都有4种工作模式 准双向口 弱上拉 推挽输出 强上拉 高阻输入 电流不能流入也不能流出 开漏输出 P30 P
  • C/C++语言中的注释:功能、符号和使用方法详解

    目录 引言 注释的功能 注释符号 单行注释 多行注释 注释结尾问题 利用预处理实现多行注释 示例代码和解析 结论 引言 在C语言中 注释是一种非常有用的工具 可以帮助程序员在代码中添加说明 解释和备注 本文将深入探讨注释的功能 不同注释符号
  • MAC中文版 FCPX V10.6.5 专属视频剪辑后期工具及其插件安装使用教程

    Final Cut Pro X简介 Final Cut Pro X又名FCPX 是MAC上非常不错的视频非线性剪辑软件 它剪辑速度超凡 具有先进的调色功能 HDR 视频支持 以及 ProRes RAW 让剪辑 音轨 图形特效 整片输出 支持
  • 网络 ip tcp/udp dhcp dns rip/ospf

    网络 七层网络模型 物理层 数据链路层 网络层 传输层 会话层 表示层 应用层 物理层定义了一系列传输介质的电气标准 这个是弱电工程师关心的 数据链路层 封装成帧 差别检错 透明传输 MAC地址 通过CRC循环冗余校验生成校验码 放在数据包
  • 44黑马QT笔记之IP地址的划分与是否在同一网段

    44黑马QT笔记之IP地址的划分与是否在同一网段 前提 1 网络ID ip地址的第一个字节 2 网络地址 在这里你可以认为它就是网络ID 3 网段 用来区分网络上的主机是否在同一区段内 只要知道ip地址和子网掩码就知道该网段 在局域网中只有
  • MySQL多字段去重

    创建学生成绩表grade grade表的字段说明 id表示学生编号 name表示学生姓名 gender表示学生性别 score表示学生分数 create table grade id int name char 1 gender char
  • 自动化测试学习路线

    1 前端开发基础 HTML JS CSS 2 浏览器调试工具 F12 FireBug Chrome浏览器 3 接口测试工具使用 PostMan SoapUI Jmeter HttpClient UrlConnection Requests
  • ubuntu下编译linux内核

    1 下载linux内核源文件 www kernel org 2 安装有关编译工具 sudo apt get install build essential kernel package libncurses5 dev 3 把内核复制到 us
  • 【老生谈算法】基于matlab的车牌识别算法详解及程序源码——车牌识别算法

    基于matlab的车牌识别系统设计与算法原理 大家好 今天给大家介绍基于matlab的车牌识别系统设计与原理 车牌识别系统 License Plate Recognition 简称LPR 是智能交通系统 ITS 的核心组成部分 在现代交通管
  • 组件不更新怎么办!??

    适合多日 碰到了个莫名其妙的问题 上传图片后 列表组件没有更新 非要刷新页面或者切换组件才能更新 之前的暂时解决方案是 上传图片后手动刷新页面 非常不友好的交互 终于忍不住了 想办法解决它 想了很多办法 怎么都没有办法刷新页面 最后突然想到
  • python不同数据类型进行转换

    代码实现 代码如下 示例 name 张三 age 20 print 我叫 name 今年 str age 岁 不同类型转换为str类型 a 10 b 198 8 c False print str a str b str c 转为字符串格式
  • chainWebpack之optimization.splitChunks的cacheGroups缓存组代码分块实践案

    研究了好几天webpack打包 我们项目是vue的高版本 已经没有了webpack config js文件了 是直接在vue config js里的chainWebpack方法直接配置 这样做法的好处是用户既可以保留webpack的默认配置
  • sql 列求和_比较几种条件求和的方法——推荐PowerBI

    比较几种条件求和的方法 1 Excel鼠标框选 配合使用筛选功能 界面右下角显示求和结果 优点 可以快速 直观地满足一次简单的业务需求 缺点 只能快速地满足一次比较简单的查询需求 且求和结果无法被记录 2 Excel公式 sum sumif
  • 不支持请求方法POST或GET的一种解决方法

    Request method POST not supported 已解决 该错误一般是请求类型对不上导致的 比如PostMapping和GetMapping请求 一般错误发生在下图所示位置 我把Post和Get搞错了 RequiresAu
  • 线程的几种状态

    目录 前言 一 线程是什么 二 线程状态 1 新建状态 New 2 就绪状态 Runnable 3 运行状态 Running 4 阻塞状态 Blocked 5 等待状态 超时等待 Waiting Timed Waiting sleep 和
  • HTTP1.0、HTTP1.1 和 HTTP2.0 的区别

    原文 https mp weixin qq com s GICbiyJpINrHZ41u 4zT A 一 HTTP的历史 早在 HTTP 建立之初 主要就是为了将超文本标记语言 HTML 文档从Web服务器传送到客户端的浏览器 也是说对于前
  • 域名简单认识

    什么是域名 域名 Domain Name 是由一串用 点 分隔的字符组成的Internet上某一台计算机或计算机组的名称 用于在数据传输时标识计算机的电子方位 有时也指地理位置 地理上的域名 指代有行政自主权的一个地方区域 域名是一个IP地
  • SQL学习(五)查询结果过滤和排序

    如果初学 看看基础语法直接结合例子来看更容易理解 基础语法 DISTINCT 选取出唯一的结果的语法 SELECT DISTINCT column another column FROM mytable WHERE condition s
  • Retrofit上传/下载文件 (一)

    Retrofit是Square公司开源的简化 HTTP 请求的库 这篇文章主要介绍用Retrofit实现文件的上传与下载的功能 本文使用的是Retrofit 2 0 2版本 1 文件上传 api service接口 public inter