首页 > 其他分享 >Springboot使用Thumbnailator压缩图片上传到阿里云OSS

Springboot使用Thumbnailator压缩图片上传到阿里云OSS

时间:2024-07-29 18:09:34浏览次数:8  
标签:return String OSS Thumbnailator Springboot new 上传 图片

原文链接:https://blog.csdn.net/weixin_39973810/article/details/86545054

前提:
图片的压缩大致有两种,一种是将图片的尺寸压缩小,另一种是尺寸不变,将压缩质量,一般对于项目我们需要第一种,即用户上传一张分辨率为3840 × 2160的图片,通过上传图片接口后上传到OSS上的图片分辨率会变成1920×1080(如3840 × 2160的图片大小为11.4M,上传后的图片大概会为1.9M),此时上传后到OSS的图片和原图质量上一致,也就是说看上去只的大小的区别,清晰度上没有任何区别,如还希望图片再小点再进行质量压缩。

整体思路:
用户点击上传图片按钮,调用上传接口,通过springboot的MultipartFile接口接收到文件,再将MultipartFile转化成一个文件放到项目中待使用(路径自己指定)。
通过指定文件路径获取到该文件(contextPath是步骤1中转存图片的文件路径),使用Thumbnails对图片进行尺寸压缩和格式转换(将图片转为jpg),然后将压缩后的图片替换步骤1中的图片。
再次通过File tempFile = new File(contextPath)获取压缩后的图片文件,将图片转为inputStream流上传至OSS,将项目中的图片文件删除,完成。
测试效果:
调用上传图片接口:

接口返回结果:

{
"success": true,
"status": "200",
"msg": "上传图片成功",
"data": "bookService/pictureUploadTest/b42dd004ddae4b329b91de0472b26163/原图11.4M(3840×2160.jpg"
}
1
2
3
4
5
6

OSS上压缩后的图片


项目代码:
控制器

/**
* 上传图片(通用)
*
* @param file 文件
* @param id 图书id
* @param folderSecond 设置二级图片路径(例bookUpload/)
* @return
*/
@RequestMapping(value = "pictureFile", method = RequestMethod.POST)
@ResponseBody
public JsonResult uploadPicture(MultipartFile file, String id, String folderSecond) {
try {
String pictureUrl = uploadService.uploadPicture(file, id, folderSecond);
log.info("上传后的图片地址:" + pictureUrl);
return renderSuccess("上传图片成功", pictureUrl);
} catch (ProgramException p) {
log.error("上传图片失败." + p.getMessage());
return renderError(p.getMessage());
} catch (Exception e) {
log.error("上传图片失败." + e.getMessage());
return renderError("上传图片失败");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
实现类:
注意:使用Thumbnailator进行格式转换的时候如果图片格式是png,在转成jpg后会出现图片变红的bug,所以此处的处理是不处理,也就是如果图片是后缀是png的话,不进行格式转换,只进行大小压缩


@Override
public String uploadPicture(MultipartFile multipartfile, String id, String folderSecond) throws Exception {
if (multipartfile == null || id == null || folderSecond == null) {
throw new ProgramException("上传图片参数不合法");
}
if (multipartfile.getSize() > 50 * 1024 * 1024) {
throw new ProgramException("上传图片大小不能超过50M!");
}

//设置统一图片后缀名
String suffixName;

//获取图片文件名(不带扩展名的文件名)
String prefixName = getFileNameWithoutEx(multipartfile.getOriginalFilename());

//获取图片后缀名,判断如果是png的话就不进行格式转换,因为Thumbnails存在转png->jpg图片变红bug
String suffixNameOrigin = getExtensionName(multipartfile.getOriginalFilename());

if ("png".equals(suffixNameOrigin)) {
suffixName = "png";
} else {
suffixName = "jpg";
}

//图片存储文件夹
String filePath = "web/src/main/resources/";

//图片在项目中的地址(项目位置+图片名,带后缀名)
String contextPath = filePath + prefixName + "." + suffixName;
//存的项目的中模版图片
File tempFile = null;
//上传时从项目中拿到的图片
File f = null;
InputStream inputStream = null;
try {
//图片在项目中的地址(项目位置+图片名,带后缀名)
tempFile = new File(contextPath);
if (!tempFile.exists()) {
//生成图片文件
FileUtils.copyInputStreamToFile(multipartfile.getInputStream(), tempFile);
}

/*
* size(width,height) 若图片横比1920小,高比1080小,不变
* 若图片横比1920小,高比1080大,高缩小到1080,图片比例不变 若图片横比1920大,高比1080小,横缩小到1920,图片比例不变
* 若图片横比1920大,高比1080大,图片按比例缩小,横为1920或高为1080
* 图片格式转化为jpg,质量不变
*/
BufferedImage image = ImageIO.read(multipartfile.getInputStream());
if (image.getHeight() > 1080 || image.getWidth() > 1920) {
if (!"png".equals(suffixName)) {
Thumbnails.of(contextPath).size(1920, 1080).outputQuality(1f).outputFormat("jpg").toFile(contextPath);
} else {
Thumbnails.of(contextPath).size(1920, 1080).outputQuality(1f).toFile(contextPath);
}
} else {
if (!"png".equals(suffixName)) {
Thumbnails.of(contextPath).outputQuality(1f).scale(1f).outputFormat("jpg").toFile(contextPath);
} else {
Thumbnails.of(contextPath).outputQuality(1f).scale(1f).toFile(contextPath);
}
}

//获取压缩后的图片
f = new File(contextPath);
inputStream = new FileInputStream(f);

//设置三级文件夹名
String folderThird = id + "/";

//设置OSS上的二级文件目录
String folderPath = folderSecond + folderThird;

//设置图片存储在oss上的名字
String fileName = prefixName + "." + suffixName;

//上传图片到OSS,返回图书路径
String resultUrl = AliyunOSSClientUtil.uploadImg2Oss(inputStream, folderPath, fileName);
return resultUrl;
} catch (Exception e) {
throw new ProgramException("图片上传失败");
} finally {
//将临时文件删除
tempFile.delete();
f.delete();
inputStream.close();
}
}

/**
* 获取文件扩展名
*
* @param filename 文件名
* @return
*/
public static String getExtensionName(String filename) {
if ((filename != null) && (filename.length() > 0)) {
int dot = filename.lastIndexOf('.');
if ((dot > -1) && (dot < (filename.length() - 1))) {
return filename.substring(dot + 1);
}
}
return filename;
}

//删除oss上的文件(该博客没有涉及到该方法,可以跳过)
@Override
public boolean deleteOssFile(String id) throws Exception {
if (id == null) {
throw new ProgramException("删除OSS文件参数不合法");
}
//设置文件位置的二级文件夹名
String folderSecond = "bookUpload/";

//设置三级文件夹名
String folderThird = id;

//设置OSS上要删除的的二级文件目录(例:client/45e233d07c664b93b7bb35331285a8d8)
String folderPath = folderSecond + folderThird;

//删除OSS上的文件(文件夹+文件)
return AliyunOSSClientUtil.deleteFile2Oss(folderPath);
}

/**
* 获取不带扩展名的文件名
*
* @param filename 文件
* @return
*/
private static String getFileNameWithoutEx(String filename) {
if ((filename != null) && (filename.length() > 0)) {
int dot = filename.lastIndexOf('.');
if ((dot > -1) && (dot < (filename.length()))) {
return filename.substring(0, dot);
}
}
return filename;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
自定义OSS工具类


package com.welsee.tools;

import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.*;
import com.welsee.exception.ProgramException;
import lombok.extern.slf4j.Slf4j;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;


/**
* 阿里云OSS相关java API
*
* @author lixinyu
*/
@Slf4j
public class AliyunOSSClientUtil {


//阿里云API的内或外网域名
private static String ENDPOINT;
//阿里云API的密钥Access Key ID
private static String ACCESS_KEY_ID;
//阿里云API的密钥Access Key Secret
private static String ACCESS_KEY_SECRET;
//阿里云API的bucket名称
private static String BUCKET_NAME;
//阿里云API的文件夹名称
private static String FOLDER;

//初始化属性
static {
ENDPOINT = OSSClientConstants.ENDPOINT;
ACCESS_KEY_ID = OSSClientConstants.ACCESS_KEY_ID;
ACCESS_KEY_SECRET = OSSClientConstants.ACCESS_KEY_SECRET;
BUCKET_NAME = OSSClientConstants.BUCKET_NAME;
FOLDER = OSSClientConstants.FOLDER;
}

/**
* 获取阿里云OSS客户端对象
*
* @return ossClient
*/
private static OSSClient getOSSClient() {
// log.info("实例化阿里云OSS对象===============" + ENDPOINT + "," + ACCESS_KEY_ID + "," + ACCESS_KEY_SECRET);
return new OSSClient(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
}

/**
* 通过文件名判断并获取OSS服务文件上传时文件的contentType
*
* @param fileName 文件名
* @return 文件的contentType
*/
private static String getContentType(String fileName) {
//文件的后缀名
String fileExtension = fileName.substring(fileName.lastIndexOf("."));
if (".bmp".equalsIgnoreCase(fileExtension)) {
return "image/bmp";
}
if (".gif".equalsIgnoreCase(fileExtension)) {
return "image/gif";
}
if (".jpeg".equalsIgnoreCase(fileExtension) || ".jpg".equalsIgnoreCase(fileExtension) || ".png".equalsIgnoreCase(fileExtension)) {
return "image/jpeg";
}
if (".html".equalsIgnoreCase(fileExtension)) {
return "text/html";
}
if (".txt".equalsIgnoreCase(fileExtension)) {
return "text/plain";
}
if (".vsd".equalsIgnoreCase(fileExtension)) {
return "application/vnd.visio";
}
if (".ppt".equalsIgnoreCase(fileExtension) || "pptx".equalsIgnoreCase(fileExtension)) {
return "application/vnd.ms-powerpoint";
}
if (".doc".equalsIgnoreCase(fileExtension) || "docx".equalsIgnoreCase(fileExtension)) {
return "application/msword";
}
if (".xml".equalsIgnoreCase(fileExtension)) {
return "text/xml";
}
//默认返回类型
return "image/jpeg";
}


/**
* 封装上传到OSS服务器方法 如果同名文件会覆盖服务器上的
*
* @param inputStream 文件流
* @param folderPath OSS目录下的二级文件名
* @param fileName 文件名称 包括后缀名
* @return 出错返回"" ,唯一MD5数字签名
*/
public static String uploadImg2Oss(InputStream inputStream, String folderPath, String fileName) throws Exception {
String result = "";
// 创建上传Object的Metadata
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(inputStream.available());
//指定该Object被下载时的网页的缓存行为
metadata.setCacheControl("no-cache");
//指定该Object下设置Header
metadata.setHeader("Pragma", "no-cache");
//文件的MIME,定义文件的类型及网页编码,决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成,
//如果没有扩展名则填默认值application/octet-stream
metadata.setContentType(getContentType(fileName));
//指定该Object被下载时的名称(指示MINME用户代理如何显示附加的文件,打开或下载,及文件名称)
metadata.setContentDisposition("inline;filename=" + fileName);
String resultUrl = FOLDER + folderPath + fileName;
// 上传文件
PutObjectResult putResult = getOSSClient().putObject(BUCKET_NAME, resultUrl, inputStream, metadata);
// 设置文件的访问权限为公共读。
getOSSClient().setObjectAcl(BUCKET_NAME, resultUrl, CannedAccessControlList.PublicRead);

if (!"".equals(putResult.getETag())) {
result = resultUrl;
log.info("上传后的图片MD5数字唯一签名:" + putResult.getETag()); //可以用来验证上传的资源是否为同一个(暂时没用到)
log.info("上传阿里云OSS服务器成功");

} else {
log.error("上传阿里云OSS服务器异常");
}
inputStream.close();
getOSSClient().shutdown();
return result;
}

/**
* 删除oss上的文件,文件夹+文件夹里面的所的文件(该博客没有涉及到该方法,可以跳过)
*
* @param folderPath 设置OSS上要删除的的二级文件目录(例:(client/45e233d07c664b93b7bb35331285a8d8))
* @return
*/
public static boolean deleteFile2Oss(String folderPath) {

//oss项目名+图书位置(bookService/bookUpload/45e233d07c664b93b7bb35331285a8d8)
String prefix = FOLDER + folderPath;

// 列举文件。 如果不设置KeyPrefix,则列举存储空间下所有的文件。KeyPrefix,则列举包含指定前缀的文件。
ObjectListing objectListing = getOSSClient().listObjects(new ListObjectsRequest(BUCKET_NAME).withPrefix(prefix));
List<String> keys = new ArrayList<>();
List<OSSObjectSummary> sums = objectListing.getObjectSummaries();
for (OSSObjectSummary s : sums) {
keys.add(s.getKey());
}

//如果OSS文件上有文件的话删除,没有直接跳过
if (keys.size() > 0) {
// 删除文件夹内的文件。
DeleteObjectsResult deleteObjectsResult = getOSSClient().deleteObjects(new DeleteObjectsRequest(BUCKET_NAME).withKeys(keys));
deleteObjectsResult.getDeletedObjects();

//删除文件夹
List<String> key = new ArrayList<>();
key.add(prefix);
getOSSClient().deleteObjects(new DeleteObjectsRequest(BUCKET_NAME).withKeys(key));

}
// 关闭OSSClient。
getOSSClient().shutdown();

return true;
}

/**
* 删除oss一个文件夹中的无用图片,保留一个图片(该博客没有涉及到该方法,可以跳过)
*
* @param picturePath 有用的图片路径
* @return
*/
public static boolean deletePictureUseless(String picturePath) throws Exception {

if (picturePath == null) {
throw new ProgramException("删除oss无用图片参数不合法");
}

//文件夹路径(bookService/bookUpload/0c6fd6fbac33466e8b26e73115f80edd)
String filePath = picturePath.substring(0, picturePath.lastIndexOf("/"));

//列举所有的图片
ObjectListing objectListing = getOSSClient().listObjects(new ListObjectsRequest(BUCKET_NAME).withPrefix(filePath));
List<String> keys = new ArrayList<>();
List<OSSObjectSummary> sums = objectListing.getObjectSummaries();
for (OSSObjectSummary s : sums) {
//判断是否是图片,只删无用图片, 不删图书
if ("jpg".equals(s.getKey().substring(s.getKey().lastIndexOf(".") + 1))) {
if (!picturePath.equals(s.getKey())) {
keys.add(s.getKey());
}
}
}
log.info("要删除的图片为===============" + keys.toString());
// 删除无用图片
if (keys.size() > 0) {
DeleteObjectsResult deleteObjectsResult = getOSSClient().deleteObjects(new DeleteObjectsRequest(BUCKET_NAME).withKeys(keys));
deleteObjectsResult.getDeletedObjects();
}
return true;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
OSS配置文件:

package com.welsee.tools;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

/**
* @author lixinyu
* @class:OSSClientConstants
* @descript:阿里云注册用户基本常量
* @date:2018年11月06日 下午5:52:34
*/
@Configuration
public class OSSClientConstants {
//阿里云API的外网域名
public static String ENDPOINT;
//阿里云API的密钥Access Key ID
public static String ACCESS_KEY_ID;
//阿里云API的密钥Access Key Secret
public static String ACCESS_KEY_SECRET;
//阿里云API的bucket名称
public static String BUCKET_NAME;
//阿里云API的文件夹名称
public static String FOLDER;

@Value("${OSS_ENDPOINT}")
public void setENDPOINT(String ENDPOINT) {
OSSClientConstants.ENDPOINT = ENDPOINT;
}

@Value("${OSS_ACCESS_KEY_ID}")
public void setAccessKeyId(String accessKeyId) {
OSSClientConstants.ACCESS_KEY_ID = accessKeyId;
}

@Value("${OSS_ACCESS_KEY_SECRET}")
public void setAccessKeySecret(String accessKeySecret) {
OSSClientConstants.ACCESS_KEY_SECRET = accessKeySecret;
}

@Value("${OSS_BUCKET_NAME}")
public void setBucketName(String bucketName) {
BUCKET_NAME = bucketName;
}

@Value("${OSS_FOLDER}")
public void setFOLDER(String FOLDER) {
OSSClientConstants.FOLDER = FOLDER;
}
}

标签:return,String,OSS,Thumbnailator,Springboot,new,上传,图片
From: https://www.cnblogs.com/fswhq/p/18005578

相关文章

  • springboot+vue基于web点餐小程序的个性化推荐演示录像【程序+论文+开题】-计算机毕业
    系统程序文件列表开题报告内容研究背景随着互联网技术的飞速发展,餐饮业正经历着前所未有的变革。传统餐饮模式逐渐向智能化、数字化转型,其中,基于Web的点餐小程序凭借其便捷性、高效性和广泛的用户覆盖,成为了餐饮行业的重要服务形式。然而,在海量美食信息与多样化消费者需求......
  • springboot+vue基于web的中草药信息管理系统【程序+论文+开题】-计算机毕业设计
    系统程序文件列表开题报告内容研究背景随着中医药在全球范围内的日益普及与认可,中草药作为中医药体系的核心组成部分,其信息的系统化、标准化管理显得尤为重要。然而,当前市场上缺乏一个全面、便捷且基于Web的中草药信息管理系统,导致中草药信息零散、不易获取,既不利于中医药......
  • 手动创建springboot项目工程
    不知道为什么基于官方的架构直接创建springboot项目工程会很慢(而且还容易卡住)试试手动创建会不会好一些基于官方架构创建——https://www.cnblogs.com/yansans/p/18305819  newproject,选中maven工程,如图——》create注意:1.archetype选maven-archetype-quic......
  • 毕业设计:基于Springboot的农产品预售平台【代码+论文+PPT】
    全文内容包括:1、采用技术;2、系统功能;3、系统截图;4、配套内容。索取方式见文末微信号,欢迎关注收藏!一、采用技术语言:Java1.8框架:SpringBoot数据库:MySQL5.7、8.0开发工具:IntelliJIDEA旗舰版其他:Maven3.8以上二、系统功能商户管理:负责商户信息的注册、审核、编辑与注销,确保......
  • springboot整合mybatis
    第一部分:创建springboot文件 第一步:打开软件,点击file,点击new然后选择module,在右侧选择springboot第二步:选择配置和JDK以及java版本①选择maven类型②选择JDK1.8版本③选择java8版本④选择jar包类型http://t.csdnimg.cn/XeplRhttp://t.csdnimg.cn/XeplR第三步:选择......
  • 实战之oss附件批量下载---springboot 实现压缩阿里云oss附件并下载
    实战之oss附件批量下载,借鉴网上一些案例,但是没有达到预期效果,结合项目需求。实现远程将oss上的文件进行压缩,并提供给前端用户下载,经过测试完美实现该功能。@PostMapping("downLoadZip")publicvoiddownLoadZip(@RequestBodyCourseDetailVodetailVo,......
  • springboot智能课堂点名系统-计算机毕业设计源码09351
     摘要本文旨在设计和实现一个基于智能课堂点名系统的智能助手。随着高校招生规模的不断扩大和信息化技术的发展,为教师提供一款便捷、全面的点名系统具有重要意义。本系统通过整合校园各项服务资源和功能,旨在帮助教师和学生更好地适应智能课堂,提供全方位的指导和支持。本......
  • springboot中小型酒店管理系统-计算机毕业设计源码02793
    摘要随着互联网和移动技术的快速发展,酒店行业也面临着巨大的变革和机遇。传统的酒店管理方式存在着信息不透明、预订流程繁琐等问题,无法满足现代消费者对便捷、高效、个性化服务的需求。因此,开发中小型酒店管理系统具有重要的意义。本文旨在设计和实现一种功能完善、易用且可......
  • springboot系列教程(二十二):springboot整合QuartJob,实现定时器实时管理
    一、QuartJob简介1、一句话描述Quartz是一个完全由java编写的开源作业调度框架,形式简易,功能强大。2、核心API(1)、Scheduler代表一个Quartz的独立运行容器,Scheduler将Trigger绑定到特定JobDetail,这样当Trigger触发时,对应的Job就会被调度。(2)、Trigger描......
  • 一个基于 SpringBoot + Vue 复刻高仿B站的视频网站!
    大家好,我是Java陈序员。今天,给大家介绍一个开源的视频网站,复刻高仿B站!关注微信公众号:【Java陈序员】,获取开源项目分享、AI副业分享、超200本经典计算机电子书籍等。项目介绍Teriteri——一个采用前后端分离的模式,参考BilibiliPC端,基于SpringBoot+Vue3实现的弹幕视......