SpringBoot 2.x 图片上传本地 静态资源配置 Docker 部署差异
概述
- 项目基于 SpringBoot 2.X
- 实现图片上传到本地并通过 ip + 端口访问
- 部署方式为 docker,同时兼容 windows 环境
- 上传的图片实现持久化保存
- 已应用到实际开发环境中
本文目的
网上有很多类似的介绍,但都是基于 window 环境的配置方式,配置的方式有好多种,这里我整合了一下,并且结合自身开发需求进行了相应的调整。
图片上传功能
- Controller
@ApiOperation("图片上传")
@ApiResponses({
@ApiResponse(code = 0, message = "图片上传成功", response = ImageDTO.class),
@ApiResponse(code = 0002, message = "图片上传异常"),
@ApiResponse(code = 0004, message = "图片过大,最大不能超过10M")
})
@PostMapping("/uploadImage")
public ResultResponse<ImageDTO> uploadImage(@RequestBody MultipartFile file) {
try {
// 1. 图片上传
String fileName = file.getOriginalFilename();
String newFileName = UuidUtils.uuid() + fileName.substring(fileName.lastIndexOf("."));
String imgFilePath = FileUtils.getSystemImagePath() + newFileName;
log.info("uploadImage path = {}", imgFilePath);
file.transferTo(new File(imgFilePath));
//2. 将图片地址返回
ImageDTO uploadDTO = new ImageDTO();
// 相对路径
String imageRelativelyUrl = AresConst.imagePath + newFileName;
// 项目地址+文件路径
String imageFullPath = serverConfig.getLocalServerUrl() + imageRelativelyUrl;
uploadDTO.setImageUrl(imageFullPath);
uploadDTO.setImageName(newFileName);
uploadDTO.setImageRelativelyUrl(imageRelativelyUrl);
return ResultResponse.success(uploadDTO);
} catch (IOException e) {
log.error(SystemEnum.IMAGE_UPLOAD_ERROR.getMsg(), e);
throw new CustomException(SystemEnum.IMAGE_UPLOAD_ERROR.getCode(), SystemEnum.IMAGE_UPLOAD_ERROR.getMsg());
}
}
- 获取系统路径方法
- ResourceUtils.getURL(“classpath:”).getPath(); 这个方法是获取 classpath 路径,也就是 resource 目录下, window 和Linux 环境下都能拿到当前项目的根路径,如果是部署在 docker 环境下,使用 jar 包启动,这个目录就为 jar 包的同级目录
Resource resource = new ClassPathResource(“classpath”); 这个方法,我当时在本地使用没有任何问题,但是部署到 docker 环境下,就获取不到路径了。
- images 在根目录下创建一个 images 文件夹,这个是为了统一管理,而且部署到 docker 后做目录挂载使用,所有图片上传到这个文件夹下
- 还有一种获取项目根目录的方法:System.getProperty(“user.dir”); 这个在 Linux 环境下同样适用,包括部署到 docker 中
/** *
* 获取图片存放路径
* @param
* @return java.lang.String
* @date 2020/06/12 11:48
*/
public static String getSystemImagePath() {
String imagePath = "";
try {
//获取根目录
File path = new File(ResourceUtils.getURL("classpath:").getPath());
if (!path.exists()) {
path = new File("");
}
//如果上传目录为/images/,则可以如下获取
File upload = new File(path.getAbsolutePath(), "images/");
if (!upload.exists()) {
upload.mkdirs();
}
// 需要拼接一个 / 上面方法生成的路径缺少/
imagePath = upload.getAbsolutePath()+"/";
} catch (FileNotFoundException e) {
log.error("getSystemImagePath 异常", e);
}
log.info("getSystemImagePath generate path = {}", imagePath);
return imagePath;
}
静态资源配置
-
主要是为了将上传后的图片提供给前端访问
-
有 2 种配置方式:
2.1 SpringMvc 的配置方式,在 application.yml 文件中配置,我下面会给出链接,可以参考
2.2 继承 WebMvcConfigurationSupport 使用代码来配置,这种方式在 SpringBoot 中比较推荐
本文采用的就是第二种方式
-
核心方法:addResourceHandlers
3.1 addResourceHandler: 这个方法里面就是配置静态资源的,addResourceHandler:匹配规则,主要是访问图片的路径,可以写正则
3.2 addResourceLocations: 静态资源路径,这个我个人认为不能写正则,其实我还是很想写正则,但是发现写了正则,然而并没有作用,或者写的方式有问题,写正则的目的是想解决一个文件夹下多级路径下的图片
window环境下:本地自定义路径(看注释),docker 或 Linux 下:linux jar 同目录下路径(看注释)
支持配置多个路径,classpath 开头的是配置到项目 resource 目录下,file 开头的是配置到自定义目录下,这个目录需要自己创建出来,如果 docker 中是以 jar 包方式启动,建议用 file 方式,如果重新部署图片也不会丢失
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.QuoteFieldNames,
SerializerFeature.WriteEnumUsingToString,
SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteDateUseDateFormat,
SerializerFeature.DisableCircularReferenceDetect);
fastJsonConfig.setSerializeFilters((ValueFilter) (o, s, source) -> {
boolean bool = source != null && (source instanceof Long || source instanceof BigInteger) && source.toString().length() > 15;
if (bool) {
return source.toString();
} else {
return null == source ? "" : source;
}
});
//处理中文乱码问题
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
fastConverter.setSupportedMediaTypes(fastMediaTypes);
fastConverter.setFastJsonConfig(fastJsonConfig);
converters.add(fastConverter);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 静态资源映射路径:文件必传放在resource/images/ eg:http://ip:port/images/4f48204eb5bd4711ad62d1a8fb42952c.jpg
// registry.addResourceHandler("/images/**").addResourceLocations("classpath:/images/");
// 本地自定义路径
// registry.addResourceHandler("/images/**").addResourceLocations("file:/F:/images");
// linux jar 同目录下路径
registry.addResourceHandler("/images/**").addResourceLocations("file:/usr/src/app/images/");
registry.addResourceHandler("/**").addResourceLocations(
"classpath:/static/");
registry.addResourceHandler("swagger-ui.html").addResourceLocations(
"classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations(
"classpath:/META-INF/resources/webjars/");
super.addResourceHandlers(registry);
}
/**
* 注册自定义拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AresInterceptor())
.addPathPatterns("/*/v1/**")
.excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**", "/favicon.ico");
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false);
}
/**
* 解决跨域问题
*
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("*")
.allowCredentials(true)
.allowedMethods("HEAD", "OPTIONS", "GET", "POST", "PUT", "PATCH", "DELETE")
.maxAge(3600);
}
}
docker 部署注意事项
- 需要挂载文件夹目录,避免重新部署时丢失文件,这个是因为 docker 每次部署会重新启动,所有的东西都是新的,所以你需要挂载到 Linux 下,启动后文件夹就是互通的
- 关于 docker 下目录,我上面给出的目录是我们的真实目录,可能不同部署方式目录会有所不一样,需要自己进入 docker 查看 jar 包所在目录地址
- docker 启动目录挂载命令: -v /data/appdatas/project/images:/usr/src/app/images
- 进入 docker 命令:docker exec -it [dockerID] /bin/sh
本文参考以下链接