摘要:
介绍MultipartFile和File之间的转换方式,以及上传单文件、多文件时的前端后端处理方式,以及文件在服务之间采用HttpClient和RestTemplate的传输应用
涉及流处理操作可以查看另一篇文章:
目录
3、服务间单、多文件转发:Restemplate、HttpClient
1、单文件多文件上传
- 先写一个简单的html用于上传文件
- 起一个简单的we服务用于接收文件
1、前端代码,复制于.txt文件后修改后缀为.html后双击打开即可
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>文件上传测试</title>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<h3>单文件ajax上传</h3>
<div class="modal-header">
<form id="addForm" name="addForm" enctype="multipart/form-data">
<!--<input type="file" name="file" id="upload_file" multiple="multiple"/>-->
<input type="file" name="file" id="upload_file" />
</form>
</div>
<button onClick="upload(1)"> 上传</button>
<h3>多文件ajax上传</h3>
<div class="modal-header">
<form id="addForm" name="addForm" enctype="multipart/form-data">
<input type="file" name="file" id="upload_file_multiple" multiple="multiple"/>
</form>
</div>
<button onClick="upload(2)"> 上传</button>
</body>
<script>
function upload(type){
var formData = new FormData();
if(type==1){
var files= $("#upload_file")[0].files;
formData.append("file",files[0]);
}else if(type==2){
var files= $("#upload_file_multiple")[0].files;
for(var i=0;i<files.length;i++){
formData.append("file",files[i]);
}
}
$.ajax({
url : "http:127.0.0.1:8088/files/uploadFile",
type : "POST",
contentType: false,
processData: false,
data:formData ,
success : function(result) {console.log(result)
},error : function(result) {console.log(result)}
});
}
</script>
</html>打开效果:

后端接收代码,单文件接收、多文件接收
接收到MultipartFile[]文件组后,可以转换成File文件进行业务操作
@Controller
@RequestMapping("/files")
public class FileController {
//单文件接收
@CrossOrigin
@ResponseBody
@RequestMapping(value = "/uploadFile", method = RequestMethod.POST )
public String uploadFile(@RequestParam(value = "file" ) MultipartFile file)throws IOException {
System.out.print("test");
return null;
}
//多文件接收
@CrossOrigin
@ResponseBody
@RequestMapping(value = "/uploadFile", method = RequestMethod.POST )
public String uploadFile(@RequestParam(value = "file" ) MultipartFile[] file)throws IOException {
System.out.print("test");
MultipartFile firstFile=file[0];
return null;
}
}2、MultipartFile、File之间的相互转换
1、MutipartFile转File
获取MultipartFile的输入字节流
通过输入字节流获取文件字节内容、输出生成一个临时的File文件(不存在编码与解码,所以不用担心乱码问题)
对File文件进行业务操作处理
删除临时的File文件
/**
* 查看MultipartFile代码可知:可以通过getInputStream()方法获取MultipartFile字节流
* 既然有了字节流,那一切皆有可能了
* 用最常规的字节流处理方式,输出一个File文件就可以了
* 需要注意的是这种方式会把文件输出到磁盘项目目录下,File使用完后需要手动删除deleteTempFile()
* @param file
* @return
*/
public static File multipartFileToFIle(MultipartFile file){
try {
File toFile=null;
if (file.equals("") || file.getSize() <= 0) {
file = null;
} else {
InputStream ins = null;
ins = file.getInputStream();
toFile = new File(file.getOriginalFilename());
inputStreamToFile(ins, toFile);
ins.close();
}
return toFile;
}catch (Exception e){
throw new RuntimeException(e);
}
}
/**
* @param ins :输入流
* @param file :输出file对象
*/
private static void inputStreamToFile(InputStream ins, File file) {
try {
OutputStream os = new FileOutputStream(file);
int bytesRead = 0;
byte[] buffer = new byte[8192];
//把字节流读到缓冲区buffer,从缓存区的坐标0开始放,放到8192
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
//关闭输入输出流
os.close();
ins.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 删除本地临时文件
* @param file :删除file对象
*/
public static void deleteTempFile(File file) {
if (file != null) {
File del = new File(file.toURI());
del.delete();
}
}2、File转MultipartFile
引入依赖 spring-test 使用 MockMultipartFile对象进行转换
发现代码相对简单,只是输入了文件名、类型、文件字节数据。却要为一个对象引入一个依赖
查看MockMultipartFile代码,发现其只是简单实现了MultipartFile接口,关键就在于下面方法的几个参数
因此我们可以自己来实现这个对象,不需要引入依赖,最简单的就是复制 MockMultipartFile的代码并把不必要的删掉
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>public static MultipartFile fileToMultipartFile(File file)throws IOException{
FileInputStream input = new FileInputStream(file);
MultipartFile MockMultipartFile=new MockMultipartFile("file", file.getName(), "text/plain", IOUtils.toByteArray(input));
return MockMultipartFile;
}改造后:不需要引入依赖,可以发现MultipartFile是定义了该类对象规范的接口,核心就在于名称、类型、字节数据等几个关键属性
回顾上述的MultipartFile转File 也是抓取字节数据 进行处理
针对 IOUtils.toByteArray这个方法,我们可以对文件做很多事情,下面的服务间传输可以验证
public static MultipartFile fileToMultipartFileT(File file)throws IOException{
FileInputStream input = new FileInputStream(file);
MultipartFile MyMultipartFile =new MyMultipartFile("file", file.getName(), "text/plain", IOUtils.toByteArray(input));
return MyMultipartFile;
}
public class MyMultipartFile implements MultipartFile {
private final String name;
private String originalFilename;
@Nullable
private String contentType;
private final byte[] content;
public MyMultipartFile(String name, @Nullable String originalFilename, @Nullable String contentType, @Nullable byte[] content) {
Assert.hasLength(name, "Name must not be null");
this.name = name;
this.originalFilename = originalFilename != null ? originalFilename : "";
this.contentType = contentType;
this.content = content != null ? content : new byte[0];
}
public String getName() {
return this.name;
}
public String getOriginalFilename() {
return this.originalFilename;
}
@Nullable
public String getContentType() {
return this.contentType;
}
public boolean isEmpty() {
return this.content.length == 0;
}
public long getSize() {
return (long)this.content.length;
}
public byte[] getBytes() throws IOException {
return this.content;
}
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(this.content);
}
public void transferTo(File dest) throws IOException, IllegalStateException {
FileCopyUtils.copy(this.content, dest);
}
}3、服务间单、多文件转发:Restemplate、HttpClient
服务间单、多文件转发
RestTemplate方式
public static String transferByRest(MultipartFile[] file)throws IOException {
RestTemplate restTemplate=new RestTemplate();
//设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
//设置请求体,注意是LinkedMultiValueMap
List<Object>fileList=new ArrayList<>();
MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
for(MultipartFile multipartFile : file) {
ByteArrayResource byteArrayResource = new ByteArrayResource(multipartFile.getBytes()){
@Override
public String getFilename() throws IllegalStateException {
return multipartFile.getOriginalFilename();
}
};
fileList.add(byteArrayResource);
}
form.put("multipartFile", fileList);
form.add("name","hello");
//用HttpEntity封装整个请求报文
HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(form, headers);
String result=restTemplate.postForObject("http://127.0.0.1:8089/files/test",entity,String.class);
System.out.println(result);
return "success";
}结合标题1的文件上传进行测试
上传单多文件到服务1,再通过RestTemplate转发到服务2
另起一个服务接收端:服务2
@Controller
@RequestMapping("/files")
public class FilerCtrl {
//两种接收方式都可以
@CrossOrigin
@ResponseBody
@RequestMapping(value = "/test", method = RequestMethod.POST )
public String transfer( HttpServletRequest req)throws IOException {
MultipartHttpServletRequest params = ((MultipartHttpServletRequest) req);
List<MultipartFile> files=params.getFiles("multipartFile");
return "success";
}
@CrossOrigin
@ResponseBody
@RequestMapping(value = "/test", method = RequestMethod.POST )
public String transfer( @RequestParam("multipartFile") MultipartFile[] multipartFile,@RequestParam("name") String name)throws IOException {
return "success";
}
}服务1:修改上传接收方法,调用转发方法
@CrossOrigin
@ResponseBody
@RequestMapping(value = "/uploadFile", method = RequestMethod.POST )
public String uploadFile(@RequestParam(value = "file" ) MultipartFile[] file)throws IOException {
System.out.print("test");
transferByRest(file);
return null;
}HttpClient方式:
需要引入新依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.5</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5</version>
</dependency>增加方法 transferByHttpClient
public static String transferByHttpClient(MultipartFile[] file)throws IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
try{
HttpPost httpPost = new HttpPost("http://127.0.0.1:8089/files/test");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setCharset(Charset.forName("utf-8"));
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);//设置浏览器兼容
//设置传输文件
for (MultipartFile multipartFile :file) {
String fileName = multipartFile.getOriginalFilename();
builder.addBinaryBody("files", multipartFile.getInputStream(), ContentType.MULTIPART_FORM_DATA, fileName);// 文件流
}
//设置传输数据
builder.addTextBody("data","json",ContentType.APPLICATION_JSON);
org.apache.http.HttpEntity httpEntity=builder.build();
httpPost.setEntity(httpEntity);
HttpResponse httpResult = httpClient.execute(httpPost);// 执行提交
org.apache.http.HttpEntity httpResultEntity=httpResult.getEntity();
if(httpResult.getStatusLine().getStatusCode()==200){
String result= EntityUtils.toString(httpResultEntity, Charset.forName("UTF-8"));
System.out.println(result);
return result;
}else {
return "fail";
}
}catch (Exception e){
e.printStackTrace();
return "fail";
}finally {
if(null!=httpClient){
httpClient.close();
}
}
}
服务1controller修改
//controller修改进行测试
@CrossOrigin
@ResponseBody
@RequestMapping(value = "/uploadFile", method = RequestMethod.POST )
public String uploadFile(@RequestParam(value = "file" ) MultipartFile[] file)throws IOException {
System.out.print("test");
transferByHttpClient(file);
return null;
}整个过程可结合上述的文件上传功能进行调试
回顾MultipartFile、File的转换以及上述两个方法的细节可知,文件在服务间的传递是以字节的形式进行的,
那么我们再回想起一个工具方法 IOUtils.toByteArray(input)
然后就有一个这样的想法,我们可以进行最原始的字节数据传输,这样就不用再考虑什么格式转换、编码解码等问题,测试一下
/**
* 为了方便,我们直接取本地的一个文件进行传递
* 获取该文件的字节流输出字节数据
*/
public static void main(String[] args)throws IOException{
File file=new File("F:/Excel文件/gbk.txt");
InputStream inputStream=new FileInputStream(file);
byte[] bytes=IOUtils.toByteArray(inputStream);
inputStream.close();
RestTemplate restTemplate=new RestTemplate();
String result=restTemplate.postForObject("http://127.0.0.1:8089/files/byteTest",bytes,String.class);
System.out.println(result);
}
//服务2接收端
@CrossOrigin
@ResponseBody
@RequestMapping(value = "/byteTest", method = RequestMethod.POST )
public String transfer( @RequestBody byte[] bytes)throws IOException {
File file=new File("F:/Excel文件/gbkTest.txt");
OutputStream outputStream=new FileOutputStream(file);
outputStream.write(bytes);
outputStream.close();
return "success";
}测试验证可行
更多涉及流处理操作可以查看另一篇文章:
学习内容整理记录,如有错误感谢指正,互相交流,共同进步