默认的约定
/home/fanhl/wukong-files/upload/public/imgs/avatar/cat.jpg
从public
处截取。/imgs/avatar/cat.jpg
private
与public
,数据库中某个字段,要么是保存public
下的文件,要么保存private
下文件,不能混放。如果要混放,需要有另外一个字段进行区分。sping.resources.static-locations
对应这个目录。wukong-files/└── upload├── private└── public└── imgs
StorageProperties
类中定了默认的目录//项目默认的文件存储目录@Value("${wukong.storage.upload-path:${user.home}/wukong-files/upload}")private String uploadRootLocation;private String imgDirName ="/imgs";
强烈建议正式项目不要使用默认的路径,因为代码中有 Junit 自动测试代码,每次系统自检,都会对这个目录下的文件进行自动删除
系统中提供了非常多的文件操作函数,有些是为了兼容老项目,今后要废弃的函数。所以下面只按照实际情况,列出项目中常用的函数。
功能说明 | 函数名 | 备注 |
---|---|---|
上传文件到临时目录 | ||
从临时文件复制到 imgs 目录下 | ||
从临时文件复制到 public 目录下 | ||
从临时文件复制到 public 目录下 | ||
公开文件上传后,可以通过浏览器的地址访问。具体的内部逻辑如下:
上传的文件,是先上传到/imgs/temp
文件夹中,当保存的时候,再将文件地址更新到数据库中。
同时将临时文件移动到正式的文件夹中。
例如:如果发现有/imgs/temp
就将这个文件复制到 brand 文件夹中,然后进行保存。
上传文件的代码
@PostMapping("/uploadToPublicTemp")public String uploadToPublicTemp(@RequestParam("file") MultipartFile file){String subPath = "temp";// 还有关于文件格式限制、文件大小限制,详见:中配置。Path unixPath= storageService.storeToImgsDir(file,subPath,storageService.generateRandomFilename());return StringUtils.replace(unixPath.toString(),storageProperties.getPublicDir(),"");}
复制很简单,只用一个函数就行了Path newPath=storageService.copyInImgsDir(tempFilePath,"brand");
。下面是一个junit
测试的代码。
@Test@DisplayName("上传图片文件")void testPost() throws Exception {File testFile= ResourceUtils.getFile("classpath:cat.png");MockMultipartFile mockMultipartFile=fileToMultipartFile(testFile);String tempFilePath=mockMvc.perform(MockMvcRequestBuilders.multipart("/upload/uploadToPublicTemp").file(mockMultipartFile)).andDo(print()).andExpect(status().isOk()).andReturn().getResponse().getContentAsString();Assertions.assertTrue(WkStringUtils.startsWith(tempFilePath,"/imgs/temp/"));// 模拟保存图片的函数Path newPath=storageService.copyInImgsDir(tempFilePath,"brand");Assertions.assertEquals(newPath.toString(),"/imgs/brand/"+WkStringUtils.getFilename(tempFilePath));//删除测试的临时文件String[] files={storageProperties.getPublicDir()+tempFilePath,storageProperties.getPublicDir()+newPath.toString(),};deleteFiles(files);}
@PostMapping("/uploadToPrivateTemp")public String uploadToPrivateTemp(@RequestParam("file") MultipartFile file){String subPath = "temp";// 还有关于文件格式限制、文件大小限制,详见:中配置。Path unixPath= storageService.storeToPrivate(file,subPath,storageService.generateRandomFilename());return StringUtils.replace(unixPath.toString(),storageProperties.getPrivateDir(),"");}
下面是一个单元测试的代码
@Test@DisplayName("上传文件到私有目录的过程")void uploadToPrivateTemp() throws Exception {File testFile= ResourceUtils.getFile("classpath:cat.png");MockMultipartFile mockMultipartFile=fileToMultipartFile(testFile);String tempFilePath=mockMvc.perform(MockMvcRequestBuilders.multipart("/upload/uploadToPrivateTemp").file(mockMultipartFile)).andDo(print()).andExpect(status().isOk()).andReturn().getResponse().getContentAsString();Assertions.assertTrue(WkStringUtils.startsWith(tempFilePath,"/temp/"));// 模拟保存图片的函数Path newPath=storageService.copyInPrivateDir(tempFilePath,"brand");Assertions.assertEquals(newPath.toString(),"/brand/"+WkStringUtils.getFilename(tempFilePath));//删除测试的临时文件String[] files={storageProperties.getPrivateDir()+tempFilePath,storageProperties.getPrivateDir()+newPath.toString(),};deleteFiles(files);}
调用storageService.deletePublicFile(brand.getBrandLogo());
进行文件删除。
@Overridepublic int deleteByPrimaryKey( Integer brandId){// delete the logoOptional<Brand> opt =brandDao.selectByPrimaryKey(brandId);if(opt.isEmpty()){throw new BusinessException("not find brand:"+brandId);}Brand brand = opt.get();if(!WkStringUtils.isEmpty(brand.getBrandLogo())){storageService.deletePublicFile(brand.getBrandLogo());}return brandDao.deleteByPrimaryKey(brandId);}
wukong.storage.upload-path
配置上传的根目录。
spring.web.resources.static-locations
配置上传根目录下的 public 为可以展示的文件夹:file:${wukong.storage.upload-path}/public
spring:messages:basename: i18n/messagesencoding: UTF-8web:resources:static-locations: file:${wukong.storage.upload-path}/publicwukong:core:response: antdstorage:upload-path: /home/fanhl/wukong-files/upload
在浏览器中直接访问这个目录就可以,如果是图片文件,就直接显示。如果是其他类型的文件,在 nginx 服务器上配置,可以下载。
http://localhost:8080/cat.png
由于 private 目录没有放在spring.web.resources.static-locations
,所以不能按照路径直接访问。那么只能使用 controller 来实现,下面是实现的代码。
/*** 从private目录中下载文件* http://localhost:8080/loadDownPrivateFile?fileName=brand/1.png* @param fileName 例如:/brand/1.png* @return ResponseEntity<Resource>*/@GetMapping("/loadDownPrivateFile")@ResponseBodypublic ResponseEntity<Resource> loadDownPrivateFile(String fileName) {Resource file = storageService.loadPrivateFileAsResource(cleanFileName(fileName));return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,"attachment; filename=\"" + file.getFilename() + "\"").body(file);}
针对图片软件
/*** 在浏览器中显示图片 http://localhost:8080/showPrivateImg?fileName=brand/1.png* @param fileName 例如:/brand/1.png* @return Resource*/@GetMapping(value="/showPrivateImg",produces = MediaType.IMAGE_JPEG_VALUE)@ResponseBodypublic Resource showPrivateImg(String fileName) {return storageService.loadPrivateFileAsResource(cleanFileName(fileName));}
StorageService
封装了常用的文件操作方法,具体见自定义函数库