bryan31:
前言
不知道大家在工作中有没有碰到过在代码级别中进行上传和下载呢,一般的场景为调用第三方的接口进行上传大文件和下载大文件。
我一个小伙伴最近在工作中就碰到了,他需要在代码中调用第三方 http 接口进行原始文件的上传,然后需要调用第三方接口把第三方服务处理好的数据文件下载到本地。他说其实没什么技术难度,百度了下,代码示例也很多,httpclient 就支持上传文件和下载,就是代码写的太多了,不怎么优雅。
他给我看了 httpclient 的上传代码:
String uploadUrl = "http://xxxxx.com/upload";
HttpPost httpPost = new HttpPost(uploadUrl);
FileBody fileBody = new FileBody(new File("C:/Users/Administrator/Desktop/source.excel"));
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
multipartEntityBuilder.addPart("file",fileBody);
// 设置其他参数
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new NameValuePair("Accept","application/json, text/plain, */*"));
nvps.add(new NameValuePair("Accept-Encoding","gzip, deflate, br"));
nvps.add(new NameValuePair("Accept-Language","zh-CN,zh;q=0.9"));
nvps.add(new NameValuePair("Connection","keep-alive"));
nvps.add(new NameValuePair("Content-Length","28700"));
nvps.add(new NameValuePair("Content-Type","multipart/form-data; boundary=----WebKitFormBoundarypaEfQmIQBbUrkI0c"));
nvps.add(new NameValuePair("Host","xxxxx.com"));
nvps.add(new NameValuePair("Origin","http://xxxxx.com"));
nvps.add(new NameValuePair("Referer","xxxxx.com/admin/goods_edit.html"));
nvps.add(new NameValuePair("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36"));
HttpEntity reqEntity = multipartEntityBuilder.build();
httpPost.setEntity(reqEntity);
try {
CloseableHttpResponse response = httpClient.execute( httpPost);
System.out.println("上传之后返回的状态码:"+response.getStatusLine().getStatusCode());
try {
HttpEntity resEntity = response.getEntity();
respStr = getRespString(resEntity);
EntityUtils.consume(reqEntity);
} catch (Exception e) {
e.printStackTrace();
} finally {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("resp=" + respStr);
因为要从代码里进行上传远端,需要建立一个 MultipartEntityBuilder,设置各种 header,小伙伴问我有什么框架可以提供更加优雅的写法。
其实很多框架都有更加简洁的 API,但是我还是推荐给了他最近一款比较火的框架:Forest
这个框架我以前也有写文推荐过:一款直击痛点的优秀 http 框架,让我超高效率完成了和第三方接口的对接
Forest
是一款主要致力于 http 请求各个场景的工具框架,基本上几行代码就可以解决几乎大部分的 http 的场景,api 主打易用性,提供了很多特性,符合国内开发者的习惯,而且作者更新也比较勤快。目前的稳定 release 版本可用于生产环境。
项目主页地址: https://gitee.com/dt_flys/forest
用 forest 实现上传和下载
Forest
能解决大部分 http 场景中的问题,对于上传下载,作者在最新的版本中提供了上传下载功能,能够以最简单的方式实现,极大程度方便了开发者。
对于想了解Forest
其他功能的童鞋,可以去项目主页或者我之前写的文章了解下。这里仅介绍用Forest
上传和下载的新特性。
这里以 springboot 项目为例,依赖Forest
:
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>spring-boot-starter-forest</artifactId>
<version>1.4.6</version>
</dependency>
定义RemoteDataHander
接口:
public interface RemoteDataHander{
@Post(url = "http://xxxxx.com/upload")
void upload(@DataFile("file") File file, OnProgress onProgress);
@Get(url = "http://xxxxx.com/report/xxx.zip")
@DownloadFile(dir = "${0}")
void downloadFile(String dir, OnProgress onProgress);
}
这个接口会被Forest
扫描组件在启动时扫描到并注册进 spring 容器,然后就可以像使用本地方法一样去调用进行上传和下载操作了。
参数中声明的OnProgress
参数,是一个接口,你可以去实现它去完成进度的回调:
File file = myClient.downloadFile("D:\\TestDownload", progress -> {
System.out.println("total bytes: " + progress.getTotalBytes()); // 文件大小
System.out.println("current bytes: " + progress.getCurrentBytes()); // 已下载字节数
System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%"); // 已下载百分比
if (progress.isDone()) { // 是否下载完成
System.out.println("-------- Download Completed! --------");
}
});
上传和下载都可以去实现OnProgress
的,当然你也可以不传。
一些其他参数的用法
除了上述例子的用法,Forest
也支持其他类型的文件参数和返回参数,如文件流,字节数组,MultipartFile 类型等等。用法如下:
上传:
/**
* File 类型对象
*/
@Post(url = "http://xxxxx.com/upload")
Map upload(@DataFile("file") File file, OnProgress onProgress);
/**
* byte 数组
* 使用 byte 数组和 Inputstream 对象时一定要定义 fileName 属性
*/
@Post(url = "http://xxxxx.com/upload")
Map upload(@DataFile(value = "file", fileName = "${1}") byte[] bytes, String filename);
/**
* Inputstream 对象
* 使用 byte 数组和 Inputstream 对象时一定要定义 fileName 属性
*/
@Post(url = "http://xxxxx.com/upload")
Map upload(@DataFile(value = "file", fileName = "${1}") InputStream in, String filename);
/**
* Spring Web MVC 中的 MultipartFile 对象
*/
@PostRequest(url = "http://xxxxx.com/upload")
Map upload(@DataFile(value = "file") MultipartFile multipartFile, OnProgress onProgress);
/**
* Spring 的 Resource 对象
*/
@Post(url = "http://xxxxx.com/upload")
Map upload(@DataFile(value = "file") Resource resource);
下载:
/**
* 返回类型用 byte[],可将下载的文件转换成字节数组
*/
@GetRequest(url = "http://localhost:8080/images/test-img.jpg")
byte[] downloadImageToByteArray();
/**
* 返回类型用 InputStream,用流的方式读取文件内容
*/
@Request(url = "http://localhost:8080/images/test-img.jpg")
InputStream downloadImageToInputStream();
其中下载返回的字节数组和输入流,可以用于自定义操作
一些感受
从使用者角度去出发,Forest
给了一个非常友好的 api,而且声明和配置都非常简单。极大程度的方便了开发者。不光上传下载场景,在其他常用的 http 的调用场景中,Forest
也面面俱到,是一个 http 层面一站式解决式工具。有兴趣的同学可以去看看,一定会提高你 http 场景的开发效率。
我也曾和作者探讨过当下 http 领域的一些框架以及Forest
的发展路线。作者比较谦虚,回答了我一些问题
作者一直都表示,希望把各种 http 的场景做到极致,使开发者真正能用几行代码就能优雅的实现复杂的 http 的场景,做开源项目不易,为这种工匠精神点赞。希望 forest 以后能为更多开发者在工作中解决痛点。
关注作者
觉得有用的话,请关注下我的公众号「元人部落」,作者坚持原创的内容技术分享,也有开源作品,欢迎关注
开源仓库为: https://gitee.com/bryan31
公众号一般周更,分享科技谈开发技术,陪你一起成长
关注后回复“资料”领取 50G 的视频资料,包括一套企业级微服务的视频教学
两分钟内,搞个实时推送aniulee: 你还在为调试 BUG 而烦恼吗?线上项目有异常,怎么实时收到通知,并及时修改呢?来吧骚年。该对接推送,有两种方式。一是对接微信公众号推送,二是网页版 websocket 推送。我把这两者结合起来,只需要一个 http 请求即可,收到推送。微信测试号,每个人都有一个。只要打开链接,微信扫码一下即可. 推送配置地址: https://www.a…
通过持久连接实现与POST(HTTP)的推送技术连接 - java我正在开发短信类型的应用程序。消息仅在((1)Server->(n)Client)方向上传递伺服器:PHP(LAMP)客户端:Android(Java)一种选择是每隔x秒向服务器请求“新消息?”。这个选项不值得我这样做,因为每x秒为每个设备创建一个HTTP连接非常昂贵。例如,每5个段产生10,000个设备创建新连接。此选项就像系统现在正在运行。选项二是…
File.Exists是否不能在UWP项目中使用? - c#File.Exists(filePath);在控制台应用程序中可以完美地工作,但是当我在uwp中执行相同的操作时,它不会检测到文件。我试图在各种方法上设置断点,并尝试任何可以为我提供有关此问题的信息的方法,但是无论我尝试什么,我都一无所获。来自UWP应用的代码: string path = @"C:\Users\Name\Desktop\image…
测一下怎么发图。。MJZ1995:https://imgur.com/fhFErjT.jpg https://imgur.com/fhFErjT.pngMJZ1995:https://imgur.com/a/hg2ZlyY.png MJZ1995:https://imgur.com/a/hg2ZlyY.jpg
jQuery File Upload插件-空文件上传结果 - php在过去的几天里,我一直在使用this插件进行自定义,并将其设置为可以完全按照我的站点要求工作。我在本地服务器上一切正常,没有错误/错误。但是,当我尝试将其上传到我的Plesk服务器时,没有文件显示在加载状态(即使目标文件夹中有图像),并且当我尝试上传im时,出现“空文件上传结果”错误。奇怪的是,即使显示了错误,文件上传似乎仍然起作用,并且相关的图像出现在文件…