代码级别的上传下载神器 - V2EX
bryan31
V2EX    Java

代码级别的上传下载神器

  •  
  •   bryan31 Sep 8, 2020 3753 views
    This topic created in 2071 days ago, the information mentioned may be changed or developed.

    前言

    不知道大家在工作中有没有碰到过在代码级别中进行上传和下载呢,一般的场景为调用第三方的接口进行上传大文件和下载大文件。

    我一个小伙伴最近在工作中就碰到了,他需要在代码中调用第三方 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

    1

    用 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的发展路线。作者比较谦虚,回答了我一些问题

    2

    3

    作者一直都表示,希望把各种 http 的场景做到极致,使开发者真正能用几行代码就能优雅的实现复杂的 http 的场景,做开源项目不易,为这种工匠精神点赞。希望 forest 以后能为更多开发者在工作中解决痛点。

    关注作者

    觉得有用的话,请关注下我的公众号「元人部落」,作者坚持原创的内容技术分享,也有开源作品,欢迎关注

    开源仓库为: https://gitee.com/bryan31

    公众号一般周更,分享科技谈开发技术,陪你一起成长

    关注后回复“资料”领取 50G 的视频资料,包括一套企业级微服务的视频教学

    img

    6 replies    2020-09-09 14:07:33 +08:00
    gz911122
        1
    gz911122 &nsp;
       Sep 8, 2020
    retrofit 了解一下
    w292614191
        2
    w292614191  
       Sep 8, 2020
    httpClient 封装成 Util 。
    你的项目也封装成 Util 。
    有啥优势呢?
    sty
        3
    sty  
       Sep 8, 2020
    和 retrofit 比有什么优势?
    source
        4
    source  
       Sep 8, 2020
    PgDn 按到底,看到二维码圆润的退出,打扰了
    MinQ
        5
    MinQ  
       Sep 8, 2020
    看到二维码,打扰了
    fewok
        6
    fewok  
       Sep 9, 2020
    嗯,包装下就是神器?
    回头我把 jackson 也包装下,json 新神器
    About     Help     Advertise     Blog     API     FAQ     Solana     1190 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 46ms UTC 23:21 PVG 07:21 LAX 16:21 JFK 19:21
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86