MultipartFile、File相互转换,单、多文件上传、以及文件在服务间HttpClient、RestTemplate转发

摘要:

介绍MultipartFile和File之间的转换方式,以及上传单文件、多文件时的前端后端处理方式,以及文件在服务之间采用HttpClient和RestTemplate的传输应用


涉及流处理操作可以查看另一篇文章:

File类、字节流、字符流、编码解读整理

目录

摘要:

1、单文件多文件上传

2、MultipartFile、File之间的相互转换

3、服务间单、多文件转发:Restemplate、HttpClient


1、单文件多文件上传

  1. 先写一个简单的html用于上传文件
  2. 起一个简单的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

  1. 获取MultipartFile的输入字节流

  2. 通过输入字节流获取文件字节内容、输出生成一个临时的File文件(不存在编码与解码,所以不用担心乱码问题)

  3. 对File文件进行业务操作处理

  4. 删除临时的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

  1. 引入依赖  spring-test  使用 MockMultipartFile对象进行转换

  2. 发现代码相对简单,只是输入了文件名、类型、文件字节数据。却要为一个对象引入一个依赖

  3. 查看MockMultipartFile代码,发现其只是简单实现了MultipartFile接口,关键就在于下面方法的几个参数

  4. 因此我们可以自己来实现这个对象,不需要引入依赖,最简单的就是复制 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. 上传单多文件到服务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方式:

  1. 需要引入新依赖

<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";
}

测试验证可行

更多涉及流处理操作可以查看另一篇文章:

File类、字节流、字符流、编码解读整理

学习内容整理记录,如有错误感谢指正,互相交流,共同进步


版权声明:本文为qq_41066277原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。