本篇文章主要写下自己在使用Spring Boot搭建后台项目时上传文件遇到的一些问题,希望对你有所帮助。
问题一:CommonsMultipartResolver
在使用Spring MVC文件上传的时候,我们会在springmvc-config.xml中配置CommonsMultipartResolver,如下:
<!-- 配置Spring MVC文件上传-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 上传文件大小上限,单位为字节(10MB) -->
<property name="maxUploadSize">
<value>10485760</value>
</property>
<!-- 请求的编码格式,必须和JSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
<property name="defaultEncoding">
<value>UTF-8</value>
</property>
</bean>
在使用Spring Boot的时候,最开始也配置multipartResolver的bean,但是在文件上传的时候,总是得到file为null,后来查询了下官方文档的资料,才知道原来Spring Boot的web工程中已经内置了一个multipartResolver的bean,如下:
原因是Spring框架先调用了系统内置的MultipartResolver来处理http multi-part请求,这个时候http multipart的请求已经被处理掉了,后面又移交给自定义的bean,自定义的bean就获取不到相应的http multi-part请求了,所以取到的file就为null了。解决办法就是把自定义的bean给移除掉就好了。内置的multipartResolver默认的最大大小为10MB。内置的multipartResolver实现源码如下所示:
问题二:获取Path路径
在Spring MVC中,我们获取文件夹的真实路径是通过getRealPath这个方法,如下:
// 上传路径
String path = session.getServletContext().getRealPath("/upload/");
但是在Spring Boot中,从app端发送上传文件请求,获取到的路径是这样的:
写单元测试发送网络请求获取到的路径是这样的:
这说明getRealPath方法并不是很通用,只适用于部分的情况。推荐使用java.nio.file.Paths这个类来获取文件夹路径,比如:
private final Path rootLocation = Paths.get("upload");
存储文件的Service类实现如下:
@Service("saveFileService")
public class SaveFileService {
private final Path rootLocation = Paths.get("upload");
public boolean store(MultipartFile file) {
boolean isSucess = false;
try {
// 创建一个文件夹
Files.createDirectories(rootLocation);
Path savePath = this.rootLocation.resolve(file.getOriginalFilename()+".jpeg");
file.transferTo(new File(savePath.toAbsolutePath().toString()));
isSucess = true;
} catch (IOException e) {
isSucess = false;
}
return isSucess;
}
public boolean deleteAll() {
return FileSystemUtils.deleteRecursively(rootLocation.toFile());
}
public Resource loadFile(String fileName) {
Path path = rootLocation.resolve(fileName);
Resource resource = null;
try {
resource = new UrlResource(path.toUri());
if (resource.exists() || resource.isReadable()) {
return resource;
} else {
throw new RuntimeException("Load File Fail!");
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
return resource;
}
}
问题三:如何用单元测试测试文件上传
单元测试测试文件上传,主要是用到了MockMultipartFile这个类,另外还是用到了fileUpload这个类,实现代码如下:
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class UploadFileTests {
@Autowired
private MockMvc mockMvc;
@MockBean
private DocumentService documentService;
@Before
public void setUp() {
Document document = new Document();
document.setTitle("高圆圆");
document.setRemark("大美女一枚");
User user = new User();
user.setId(1);
document.setUser(user);
document.setFileName("userAvatar.jpeg");
BDDMockito.given(documentService.addDocument(document)).willReturn(true);
}
@Test
public void testUploadTest() {
SortedMap<String, String> sortedMap = new TreeMap<>();
sortedMap.put("timeStamp", System.currentTimeMillis()+"");
sortedMap.put("ownerId", "1");
sortedMap.put("title", "刘亦菲");
sortedMap.put("remark", "古典美女");
StringBuilder stringBuilder = new StringBuilder();
for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
stringBuilder.append(entry.getKey() + "=" + entry.getValue());
}
String sign = MD5Util.createMD5Sign(sortedMap, BootConstants.SIGN_KEY);
System.out.println("sign : " + sign);
sortedMap.put("sign", sign);
String mapStr = JSON.toJSONString(sortedMap, SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullStringAsEmpty);
String encryptStr = AESUtil.encrypt(mapStr, BootConstants.AES_KEY, BootConstants.AES_IV);
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("hrm2.jpg");
try {
MockMultipartFile multipartFile = new MockMultipartFile("file", "userAvatar5", "image/jpeg", inputStream);
mockMvc.perform(fileUpload("/hrm/api/documents").file(multipartFile).param("Encrypt", encryptStr))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk());
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
这样就完成了上传文件的测试,代码还是比较简单的。