在Spring boot应用中,由于图片文件比较大,一般采用数据库或者文件服务器的方式进行存储。但是常用的图片如多采用读取数据库或者文件的方式会加大系统的负载,而从物理硬盘读取图片的速度往往达不到期望。因此,将图片以字节流的形式存储在Redis中不失为一个方案。本文主要是在Spring boot中采用Redis集群存储图片。
compile 'org.springframework.boot:spring-boot-starter-web'
compile ('org.springframework.boot:spring-boot-starter-data-redis'){
exclude(group:'io.lettuce')
}
compile 'redis.clients:jedis'
testImplementation('org.springframework.boot:spring-boot-starter-test')
/**
* 将图片内容编码为字符串
* @param file
* @return
*/
public static String encodeImageToBase64(MultipartFile file) {
byte[] bytes = null;
try {
bytes = file.getBytes();
} catch (IOException e) {
e.printStackTrace();
}
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(bytes).trim();
}
/**
* 将图片内容解码为输入流
* @param base
* @return
*/
public static InputStream decodeBase64ToImage(String base) {
BASE64Decoder decoder = new BASE64Decoder();
byte[] decodeBytes = null;
try {
decodeBytes = decoder.decodeBuffer(base);
} catch (IOException e) {
e.printStackTrace();
}
return new ByteArrayInputStream(decodeBytes);
}
为了偷懒,将Controller和Redis的读取与存储都写在一起,没有再单独抽离。
/**
* 上传图片
* @param file
* @return
*/
@RequestMapping(path = "/upload", method = RequestMethod.POST)
public ResponseEntity uploadImage(@RequestParam("file") MultipartFile file) {
String id = UUID.randomUUID().toString();
// 将图片编码为字符串
String content = Base64ImageUtils.encodeImageToBase64(file);
Map criteria = new HashMap<>();
criteria.put("content", content);
criteria.put("fileName", file.getOriginalFilename());
criteria.put("size", String.valueOf(file.getSize()));
// 将图片信息以map的形式存储再redis
jedisCluster.hmset(id, criteria);
Map map = new HashMap<>();
map.put("id", id);
return ResponseEntity.ok(map);
}
/**
* 根据存储id下载图片
* @param id
* @return
*/
@RequestMapping(path = "/download/{id}", method = RequestMethod.GET)
public ResponseEntity downloadResource(@PathVariable String id) {
// 获取图片信息
Map criteria = jedisCluster.hgetAll(id);
return ResponseEntity.ok()
.headers(httpHeaders(criteria.get("size"), criteria.get("fileName")))
.cacheControl(CacheControl.maxAge(Long.MAX_VALUE, TimeUnit.HOURS).cachePublic())
.body(body(criteria.get("content")));
}
/**
* 将图片输入流转换为输入流资源
* @param content
* @return
*/
private InputStreamResource body(String content) {
return new InputStreamResource(Base64ImageUtils.decodeBase64ToImage(content));
}
/**
* 设置响应的头部信息
* @param size
* @param name
* @return
*/
private HttpHeaders httpHeaders(String size, String name) {
final HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.valueOf(configurableMimeFileTypeMap.getContentType(name)));
httpHeaders.set(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename*=UTF-8''" + encode(name));
httpHeaders.set(HttpHeaders.CONTENT_LENGTH, size);
return httpHeaders;
}
/**
* 文件名进行编码
* @param name
* @return
*/
private String encode(String name) {
try {
return URLEncoder.encode(name, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e.getMessage());
}
}
设置接口请求类型,请求接口;Body中设置为file,并选择文件,点击send开始测试。测试选择的文件大小为3.4M
{
"timestamp": "2018-12-19T04:37:52.602+0000",
"status": 500,
"error": "Internal Server Error",
"message": "Maximum upload size exceeded; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$FileSizeLimitExceededException: The field file exceeds its maximum permitted size of 1048576 bytes.",
"path": "/jedis/upload"
}
由此可知:图片太大。原因在于:默认的文件最大是1M,此时需要修改默认配置。有以下两种方式进行配置:
修改application.yml配置
在spring节点下,新增配置max-request-size和max-file-szie。但此种方式已弃用
http:
multipart:
max-request-size: 10MB
max-file-size: 10MB
修改启动类Application.java(建议)
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setMaxFileSize("10240KB"); //KB,MB /// 设置总上传数据总大小
factory.setMaxRequestSize("102400KB");
return factory.createMultipartConfig();
}
重启应用,测试结果如下。图片上传成功
{
"id": "3cca22f6-0e72-487b-8fe2-b743b6b7f3b4"
}
请求如图,将文件id作为路有参数传入
测试结果如下。图片下载成功