该随笔记录了在实际项目中使用HttpClient调用外部api,需上传文件和普通参数的代码。

笔者在使用 HttpClient 调用 http api 接口时,需要服务端上传文件和一些普通参数给 http api,如果使用 Java 自带的 HttpURLConnection 请求的话,发送 multipart/form-data + POST 请求会比较麻烦,需要设置一些边界(将文件与文件、文件与普通参数之间隔开,便于接收者截取,这是 http 协议要求的)。

因为上传文件和普通参数时,服务端读取报文是根据边界值来截取的,如果使用原生的 HttpURLConnection 则比较麻烦,所以笔者采用 HttpClient 工具,httpclient是apache 软件基金会下的子项目,它很好的封装了Http工具,面向对象的思想省去了很多细节,使程序员关注与业务逻辑处理,不用关注这些通讯细节。

笔者使用HTTPClient实现文件的上传,使用 MultipartEntityBuilder 构造请求体,实现 multipart/form-data + POST 请求http接口。下面提供了使用时的代码实现,包括服务端和客户端。

不过,笔者在使用的过程中发现,当传递的普通参数有中文时,对方接到的参数会乱码,因为开始笔者使用的是multipartEntity.addTextBody(key, postParam.get(key));的方式设置普通参数。

为了解决乱码问题,最后查到了解决办法,记录如下。

如下代码是可以上传多个文件和普通参数的,使用 multipart/form-data + POST 方式提交,模拟浏览器在页面上 form表单 的提交方式。


客户端上传文件及普通参数代码:

 /**
* httpclient 文件上传
* @param postFiles
* @param postUrl
* @param postParam
* @return
*/
public static Map<String, Object> uploadFileByHttpPost(File[] postFiles, String postUrl, Map<String, String> postParam) {
Map<String, Object> resultMap = new HashMap<String, Object>();
CloseableHttpClient httpClient = HttpClients.createDefault();
try {
//把一个普通参数和文件上传给下面这个api接口
HttpPost httpPost = new HttpPost(postUrl);
//设置传输参数,设置编码。设置浏览器兼容模式,解决文件名乱码问题
MultipartEntityBuilder multipartEntity = MultipartEntityBuilder.create().setMode(HttpMultipartMode.RFC6532);
for (int i = 0; i < postFiles.length; i++) {
File postFile = postFiles[i];
FileBody fundFileBin = new FileBody(postFile, ContentType.MULTIPART_FORM_DATA); //相当于<input type="file" name="media"/>
multipartEntity.addPart("upload_file"+i, fundFileBin);
}
//把文件转换成流对象FileBody
Set<String> keySet = postParam.keySet();
for (String key : keySet) {
//解决中文乱码
ContentType contentType = ContentType.create("text/plain", Charset.forName("UTF-8"));
StringBody stringBody = new StringBody(postParam.get(key), contentType);
multipartEntity.addPart(key, stringBody);
// multipartEntity.addTextBody(key, postParam.get(key));//这个中文会乱码
}
HttpEntity reqEntity = multipartEntity.build();
httpPost.setEntity(reqEntity);
//发起请求 并返回请求的响应
CloseableHttpResponse response = httpClient.execute(httpPost);
try {
//打印响应状态
resultMap.put("statusCode", response.getStatusLine().getStatusCode());
//获取响应对象
HttpEntity resEntity = response.getEntity();
if (resEntity != null) {
//打印响应内容
resultMap.put("data", EntityUtils.toString(resEntity, Charset.forName("UTF-8")));
}
//销毁
EntityUtils.consume(resEntity);
} catch (Exception e) {
e.printStackTrace();
} finally {
response.close();
}
} catch (ClientProtocolException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
} finally {
try {
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultMap;
}

服务端处理请求代码

这是客户端上传的代码,我们看一下怎么接收,使用SpringMVC controller层接收文件和普通参数:

 /**
* 上传文件
* @throws IOException
* @throws IllegalStateException
*/
@RequestMapping("/postFile")
@ResponseBody
public String postFile(HttpServletRequest request){
Map<String, Object> map = new HashMap<String, Object>();
// 创建一个通用的多部分解析器
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request
.getSession().getServletContext());
String name = request.getParameter("name");
String age = request.getParameter("age"); System.out.println(name+","+age);
request.getSession().getServletContext();
// 判断 request 是否有文件上传,即多部分请求
if (multipartResolver.isMultipart(request)) {
// 转换成多部分request
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
// 取得request中的所有文件名
Iterator<String> iter = multiRequest.getFileNames();
while (iter.hasNext()) {//多文件
// 取得上传文件
MultipartFile multipartFile = multiRequest.getFile(iter.next());
if (null != multipartFile) {
// 取得当前上传文件的文件名称
String fileName = multipartFile.getOriginalFilename();
if (fileName.trim() != null && fileName.trim().length() > 0) {
CommonsMultipartFile cf = (CommonsMultipartFile) multipartFile;
DiskFileItem fi = (DiskFileItem) cf.getFileItem();
File tempFile = fi.getStoreLocation();
// 拿到文件,存储
try {
multipartFile.transferTo(new File("F:\\static\\page\\"+multipartFile.getOriginalFilename()));
} catch (IOException e) {
e.printStackTrace();
return "error";
}
}
}
}
}
return "success";
}

测试代码:

 public static void main(String[] args) {
String url = "http://localhost:8080/postFile";
File[] files = new File[2];
files[0] = new File("F:\\static\\updateFile-demo.docx");
files[1] = new File("F:\\static\\updateFile-demo02.docx"); Map<String,String> param = new HashMap<>();
param.put("name","编程大道");
param.put("age","18"); Map<String, Object> stringObjectMap = HttpClientUtil01.uploadFileByHttpPost(files, url, param);
System.out.println(stringObjectMap);
}

代码验证

我们上传两个文件和两个普通参数,服务端controller里的处理是,打印这两个普通参数并把两个文件保存到page目录下

启动服务

我们先启动服务端,如下图正常启动

运行测试类,控制台输出如下:

服务端输出:

查看是否保存成功

成功!!


注意事项:

但是在成功之前也遇到了问题,如下:

上传文件:

传普通参数,注意中文乱码的问题:

HttpClient多文件上传代码及普通参数中文乱码问题解决的更多相关文章

  1. servlet3.0获取参数与文件上传代码示例

    转: servlet3.0获取参数与文件上传代码示例 2018年08月26日 20:25:35 苏凯勇往直前 阅读数:98   package com.igeek.servlet;   import ...

  2. php文件上传代码解析

    php文件上传代码解析 is_uploaded_file()  //函数判断指定的文件是否是通过 HTTP POST 上传的,返回一个布尔值. $_FILES['upfile']['tmp_name' ...

  3. HttpClient构造文件上传

    在项目中我们有时候需要使用到其他第三方的api,而有些api要求我们上传文件,search一下,下面将结果记录一下喽! 含义 ENCTYPE="multipart/form-data&quo ...

  4. 实现Magento多文件上传代码功能开发

    在Magento中上传单个文件很简单,可以直接在继承的Mage_Adminhtml_Block_Widget_Form类中直接添加如下组件Field:  对于图片:   $fieldset->a ...

  5. (实用篇)php处理单文件、多文件上传代码分享

    php处理  单文件.多文件上传实例代码,供大家参考,具体内容如下 后台处理文件submit_form_process.php <?php /************************** ...

  6. PHP文件上传代码和逻辑详解

    文件上传的逐步完善------ [简单的上传:]   <form action="upload.php"  method="post"  enctype= ...

  7. PHP 图片文件上传代码

    通过 PHP,可以把文件上传到服务器.里面加入一些图片的判断,如果不加判断文件的类型就可以上传任意格式的文件. 为了网站的安全,肯定不让上传php文件,如果有人进入你的后台,上传了一个php文件,你的 ...

  8. PHP 图片文件上传代码分享

    分享下php上传图片文件的一段代码,挺不错的. 通过 PHP,可以把文件上传到服务器.加入一些图片的判断,如果不加判断文件的类型就可以上传任意格式的文件. 当然了,会禁止上传php文件,以及其它程序代 ...

  9. ASP文件上传代码

    在网上看到的代码,稍微有点问题,改了一下就可以了.Chrome下是可以用的,别的浏览器还没有确认. <% Response.Buffer = True Server.ScriptTimeOut= ...

随机推荐

  1. SignalR---服务端

    原文:SignalR---服务端 前段时间把SignalR的官网教程大致看了一下,算是翻译了一遍,加上了自己的个人理解, 一下上传三个文件,分别是服务端.web客户端.DOTNET客户端相关文档,供大 ...

  2. Linux iostat

    转自 http://www.cnblogs.com/ggjucheng/archive/2013/01/13/2858810.html Linux IO实时监控iostat命令详解 简介 iostat ...

  3. 知识的内化:学习、实践、输出(与Focus Feedback FixIt的原理是一致的)

    一个人的能力分三个层次: 资源,比如知识.技能.经验.时间.精力.金钱.人脉等 应用流程,即使用资源解决问题的能力,包括做事的方法.流程.策略等,它是你整合应用资源创造价值的能力. 价值取向,即你觉得 ...

  4. Varnish动静分离配置示例

    动静分离 [root@varnish ~]# vim /etc/varnish/default.vclvcl 4.0;backend web { .host = "192.168.30.15 ...

  5. iOS登录及token的业务逻辑(没怎么用过,看各种文章总结)

    http:是短连接. 服务器如何判断当前用户是否登录? // 1. 如果是即时通信类:长连接. // 如何保证服务器跟客户端保持长连接状态? // "心跳包" 用来检测用户是否在线 ...

  6. happy machine learning(First One)

    从前几天起我就开始了愉快的机器学习,这里记录一下学习笔记,我看的是吴恩达老师的视频,这篇博客将会按吴老师的教学目录来集合各优良文章,以及部分的我的个人总结 1.  监督学习与无监督学习 监督:给定一个 ...

  7. spark 源码分析之二 -- SparkContext 的初始化过程

    创建或使用现有Session 从Spark 2.0 开始,引入了 SparkSession的概念,创建或使用已有的session 代码如下: val spark = SparkSession .bui ...

  8. 《实战Java高并发程序设计》读书笔记

    文章目录 第二章 Java并行程序基础 2.1 线程的基本操作 2.1.1 线程中断 2.1.2 等待(wait)和通知(notify) 2.1.3 等待线程结束(join)和谦让(yield) 2. ...

  9. linux 下 设置 MySQL8 表名大小写不敏感方法,解决设置后无法启动 MySQL 服务的问题

    在安装完成之后,初始化数据库之前,修改 my.cnf 打开mysql配置文件 vim /etc/my.cnf 在尾部追加一行 lower_case_table_names=1 并保存,然后再初始化数据 ...

  10. Spring注解之-自定义注解

    1.自定义注解,先自定义三个水果属性的注解 元注解: java.lang.annotation提供了四种元注解,专门注解其他的注解(在自定义注解的时候,需要使用到元注解):   @Documented ...