快速入门文件上传
如何还不熟悉如何使用 Spring cloud Feign 实现文件上传。
可以参考翟永超 Spring Cloud Feign的文件上传实现
本篇主要介绍如何构造MultipartFile
,首先我们通过@RequestPart("file")
和@FeignClient
等注解成功的构造出了一个上传文件的客户端。
例如:
@RequestMapping(method = RequestMethod.POST
, value = "/uploadFile"
, produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}
, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseBody
public Map<String, Object> uploadFile(@RequestPart(value = "file") MultipartFile file);
但是,如果我们需要上传一个File
对象的时候,将会发现没有合适的MultipartFile
实现类构造这个参数。
QuickStart
为了快速的构造MultipartFile
实例
我们需要使用到org.springframework.mock.web.MockMultipartFile
,所以引入
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
如果使用的是Spring boot 搭建的项目,该依赖的版本将会自动从Spring boot父母pom中继承;否则需要自己设置
version
标签指定版本。
如果不想引入额外的依赖,由于MockMultipartFile
内部没有其他而外库的引用,完全可以单独从源码中复制该文件到项目中即可,文档后面将会附上该文件的源码。
FileInputStream in = new FileInputStream("...");
MockMultipartFile multipartFile =
new MockMultipartFile("file", in);
注意:第一个参数,对应着接口中@RequestPart(value = "file")
中的value,也就是file
,如果这两个参数不一致,会导致报异常:Required request part ‘file’ is not present
该构造器的缺点是无法控制上传文件的名称,通过这种方式上传可能会造成文件名为空,特别是构造的对象是一个byte
数组的情况。
可以使用下面这种构造器:
FileInputStream in = new FileInputStream("...");
String fileName = "this_is_your_file.txt"
MockMultipartFile multipartFile = new MockMultipartFile("file"
, fileName
, null
, FileCopyUtils.copyToByteArray(in));
当然也可以直接使用字节数组来构建,构造方法如下:
public MockMultipartFile(String name, @Nullable byte[] content)
方式二
参考自:翟永超 Spring Cloud Feign的文件上传实现
FileItem fileItem = new DiskFileItemFactory().createItem("file"
, MediaType.TEXT_PLAIN_VALUE
, true
, resource.getFilename());
try (InputStream input = resource.getInputStream();
OutputStream os = fileItem.getOutputStream()) {
// 流转移
IOUtils.copy(input, os);
} catch (Exception e) {
throw new IllegalArgumentException("Invalid file: " + e, e);
}
该方式不需要而外的依赖,两种方式都复制了流中的内容到内存中,相对而言第一种方式构造相对简单,代码可读性也较高。
第一种方式将文件流中内容复制到了数组中(内存)。
第二种方式是流的转移,对于大文件更加友好。
MockMultipartFile 源码
复制与Spring-test包内org.springframework.mock.web.MockMultipartFile.java
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.mock.web;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
/**
* Mock implementation of the {@link org.springframework.web.multipart.MultipartFile}
* interface.
*
* <p>Useful in conjunction with a {@link MockMultipartHttpServletRequest}
* for testing application controllers that access multipart uploads.
*
* @author Juergen Hoeller
* @author Eric Crampton
* @since 2.0
* @see MockMultipartHttpServletRequest
*/
public class MockMultipartFile implements MultipartFile {
private final String name;
private String originalFilename;
@Nullable
private String contentType;
private final byte[] content;
/**
* Create a new MockMultipartFile with the given content.
* @param name the name of the file
* @param content the content of the file
*/
public MockMultipartFile(String name, @Nullable byte[] content) {
this(name, "", null, content);
}
/**
* Create a new MockMultipartFile with the given content.
* @param name the name of the file
* @param contentStream the content of the file as stream
* @throws IOException if reading from the stream failed
*/
public MockMultipartFile(String name, InputStream contentStream) throws IOException {
this(name, "", null, FileCopyUtils.copyToByteArray(contentStream));
}
/**
* Create a new MockMultipartFile with the given content.
* @param name the name of the file
* @param originalFilename the original filename (as on the client's machine)
* @param contentType the content type (if known)
* @param content the content of the file
*/
public MockMultipartFile(
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]);
}
/**
* Create a new MockMultipartFile with the given content.
* @param name the name of the file
* @param originalFilename the original filename (as on the client's machine)
* @param contentType the content type (if known)
* @param contentStream the content of the file as stream
* @throws IOException if reading from the stream failed
*/
public MockMultipartFile(
String name, @Nullable String originalFilename, @Nullable String contentType, InputStream contentStream)
throws IOException {
this(name, originalFilename, contentType, FileCopyUtils.copyToByteArray(contentStream));
}
@Override
public String getName() {
return this.name;
}
@Override
public String getOriginalFilename() {
return this.originalFilename;
}
@Override
@Nullable
public String getContentType() {
return this.contentType;
}
@Override
public boolean isEmpty() {
return (this.content.length == 0);
}
@Override
public long getSize() {
return this.content.length;
}
@Override
public byte[] getBytes() throws IOException {
return this.content;
}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(this.content);
}
@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
FileCopyUtils.copy(this.content, dest);
}
}