什麼是對象存儲OSS
阿里雲對象存儲OSS(Object Storage Service)是阿里雲提供的海量、安全、低成本、高可靠的雲存儲服務。其數據設計持久性不低於99.9999999999%(12個9),服務可用性(或業務連續性)不低於99.995%。
OSS具有與平臺無關的RESTful API接口,您可以在任何應用、任何時間、任何地點存儲和訪問任意類型的數據。
您可以使用阿里雲提供的API、SDK接口或者OSS遷移工具輕鬆地將海量數據移入或移出阿里雲OSS。數據存儲到阿里雲OSS以後,您可以選擇標準存儲(Standard)作爲移動應用、大型網站、圖片分享或熱點音視頻的主要存儲方式,也可以選擇成本更低、存儲期限更長的低頻訪問存儲(Infrequent Access)、歸檔存儲(Archive)作爲不經常訪問數據的存儲方式。
集成OSS
pom.xml
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
application.yml
oss:
endpoint: https://oss-cn-hangzhou.aliyuncs.com #Endpoint以杭州爲例,其它Region請按實際情況填寫
accessKeyId: yourAccessKeyId
secretAccessKey: yourAccessKeySecret
bucketName: yourBucketName
urlPrefix: 你的文件訪問地址 #bucketName+endpoint
OSS客戶端配置類,並注入到Spring中
@Data
@Configuration
@ConfigurationProperties(prefix = "oss")
//@PropertySource(value = {"classpath:application-oss.properties"})
public class OssConfig {
private String endpoint;
private String accessKeyId;
private String secretAccessKey;
private String bucketName;
private String urlPrefix;
@Bean
public OSS oSSClient() {
return new OSSClientBuilder().build(endpoint, accessKeyId, secretAccessKey);
}
}
文件上傳、刪除、下載、圖像壓縮、圖像添加水印
測試
@RestController
public class OssController {
@Autowired
private OssUtil ossUtil;
/**
* 文件上傳到oss
*
* @param files
* @return
*/
@PostMapping("/oss/upload")
public String upload(@RequestParam("file") MultipartFile file) {
return ossUtil.putObject(file);
}
/**
* 刪除文件
*
* @param fileName
* @return
*/
@GetMapping("oss/delete")
public String delete(@RequestParam("fileName") String fileName) {
Boolean flag = ossUtil.deleteObject(fileName);
if (flag) {
return "刪除成功";
}
return "刪除失敗";
}
/**
* 查詢文件列表
*
* @return
*/
@GetMapping("oss/list")
public List<OSSObjectSummary> list() {
List<OSSObjectSummary> fileList = ossUtil.listObjects();
return fileList;
}
/**
* 下載文件
*
* @param fileName
* @param response
* @throws IOException
*/
@GetMapping("oss/download")
public void download(@RequestParam("fileName") String fileName, HttpServletResponse response) throws IOException {
// 通知瀏覽器以附件形式下載
response.setHeader("Content-Disposition",
"attachment;filename=" + new String(fileName.getBytes(), "ISO-8859-1"));
ossUtil.download(response.getOutputStream(), fileName);
}
/**
* 上傳圖片
*
* @param file
* @return
*/
@PostMapping("/oss/uploadPic")
public String uploadPic(@RequestParam("file") MultipartFile file) {
boolean flag = ossUtil.isImage(file);
if (flag) {
return ossUtil.uploadPic(file);
}
return "上傳失敗";
}
}
OSS工具類
/**
* 阿里雲OSS服務器工具類
*
*/
@Component
public class OssUtil {
@Autowired
private OSS ossClient;
@Autowired
private OssConfig ossConfig;
private static final long EXPIRATION = 3600l * 1000 * 24 * 7;// 七天
// 允許上傳的格式
private static final String[] IMAGE_TYPE = new String[] { ".bmp", ".jpg", ".jpeg", ".gif", ".png" };
private String PATH = "javakf/";
/**
* 單文件上傳
*
* @param file
* @return
*/
public String uploadFile(MultipartFile file) {
String fileUrl = putObject(file);
return fileUrl;
}
/**
* 多文件上傳
*
* @param fileList
* @return
*/
public String multiUpload(List<MultipartFile> fileList) {
String url = null;
String fileUrl = null;
for (int i = 0; i < fileList.size(); i++) {
url = putObject(fileList.get(i));
if (i == 0) {
fileUrl = url;
} else {
fileUrl += ";" + url;
}
}
return fileUrl.trim();
}
/**
* 上傳文件-支持單文件,多文件 -返回文件訪問路徑,多文件以分號‘;’分隔
*
* @param muFiles
* @return
*/
public String uploadFiles(MultipartFile... file) {
if (file.length < 1) {
throw new RuntimeException("上傳文件爲空!");
}
StringBuilder str = new StringBuilder();
for (MultipartFile muFile : file) {
str.append(putObject(muFile));
str.append(";");
}
return str.deleteCharAt(str.length() - 1).toString();
}
/**
* 上傳文件
*
* @param file
* @return
*/
public String putObject(MultipartFile file) {
try {
String fileName = getFilePathName(file, true);
// 上傳到阿里雲
ossClient.putObject(ossConfig.getBucketName(), fileName, new ByteArrayInputStream(file.getBytes()));
return this.ossConfig.getUrlPrefix() + fileName;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("上傳文件失敗!");
}
}
/**
* 上傳圖片
*
* @param file
* @return
*/
public String uploadPic(MultipartFile file) {
try {
String fileName = getFilePathName(file, true);
InputStream inputStream = file.getInputStream();
// 如果是圖片文件就進行壓縮並添加水印
if (ImageUtil.isImage(file.getOriginalFilename())) {
inputStream = ImageUtil.getInputStream(
ImageUtil.setWatermark(ImageUtil.compress(ImageIO.read(inputStream))),
ImageUtil.getFileExtention(file.getOriginalFilename()));
}
// 上傳到阿里雲
ossClient.putObject(ossConfig.getBucketName(), fileName, inputStream);
return this.ossConfig.getUrlPrefix() + fileName;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("上傳文件失敗!");
}
}
/**
* 校驗圖片格式
*
* @param file
* @return
*/
public boolean isImage(MultipartFile file) {
boolean flag = false;
for (String type : IMAGE_TYPE) {
if (StringUtils.endsWithIgnoreCase(file.getOriginalFilename(), type)) {
flag = true;
break;
}
}
return flag;
}
/**
* 刪除文件
*
* @param fileName
* @return
*/
public Boolean deleteObject(String fileName) {
try {
ossClient.deleteObject(ossConfig.getBucketName(), fileName);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 查詢文件列表(默認100條)
*
* @return
*/
public List<OSSObjectSummary> listObjects() {
ObjectListing listObjects = ossClient.listObjects(ossConfig.getBucketName());
List<OSSObjectSummary> fileList = listObjects.getObjectSummaries();
return fileList;
}
/**
* 查詢文件列表(默認值100條,最大值1000條)
*
* @param maxKeys 要查詢的條數
* @return
*/
public List<OSSObjectSummary> listObjects(Integer maxKeys) {
if (maxKeys == null) {
maxKeys = 100;
}
ObjectListing listObjects = ossClient
.listObjects(new ListObjectsRequest(ossConfig.getBucketName()).withMaxKeys(maxKeys));
List<OSSObjectSummary> fileList = listObjects.getObjectSummaries();
return fileList;
}
/**
* 查詢指定前綴的文件列表
*
* @param prefix 前綴
* @return
*/
public List<OSSObjectSummary> listObjects(String prefix) {
ObjectListing listObjects = ossClient.listObjects(ossConfig.getBucketName(), prefix);
List<OSSObjectSummary> fileList = listObjects.getObjectSummaries();
return fileList;
}
/**
* 下載文件
*
* @param outputStream
* @param fileName
*/
public void download(OutputStream outputStream, String fileName) {
try {
OSSObject ossObject = ossClient.getObject(ossConfig.getBucketName(), fileName);
BufferedInputStream in = new BufferedInputStream(ossObject.getObjectContent());
BufferedOutputStream out = new BufferedOutputStream(outputStream);
byte[] buffer = new byte[1024];
int lenght = 0;
while ((lenght = in.read(buffer)) != -1) {
out.write(buffer, 0, lenght);
}
if (out != null) {
out.flush();
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 生成下載鏈接
*
* @param fileName
* @return
*/
public String getUrl(String fileName) {
if (!StringUtils.isEmpty(fileName)) {
// 設置URL過期時間爲10年 3600l*1000*24*365*10
Date expiration = new Date(new Date().getTime() + EXPIRATION);
// 生成URL
URL url = ossClient.generatePresignedUrl(ossConfig.getBucketName(), fileName, expiration);
if (url != null) {
return url.toString();
}
}
return null;
}
/**
* 上傳文件
*
* @param inputStream
* @param fileName
* @return
*/
public String putObject(InputStream inputStream, String fileName) {
try {
// 文件擴展名
String fileSuffix = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
// 新的文件名
fileName = PATH + new Random().nextInt(10000) + System.currentTimeMillis() + fileSuffix;
// 創建上傳Object的Metadata
ObjectMetadata objectMetadata = new ObjectMetadata();
// 上傳的文件的長度
objectMetadata.setContentLength(inputStream.available());
// 指定該Object被下載時的網頁的緩存行爲
objectMetadata.setCacheControl("no-cache");
// 指定該Object下設置Header
objectMetadata.setHeader("Pragma", "no-cache");
// 指定該Object被下載時的內容編碼格式
objectMetadata.setContentEncoding("utf-8");
// 文件的MIME,定義文件的類型及網頁編碼,決定瀏覽器將以什麼形式、什麼編碼讀取文件。如果用戶沒有指定則根據Key或文件名的擴展名生成,
// 如果沒有擴展名則填默認值application/octet-stream
objectMetadata.setContentType(getcontentType(fileSuffix));
// 指定該Object被下載時的名稱(指示MINME用戶代理如何顯示附加的文件,打開或下載,及文件名稱)
objectMetadata.setContentDisposition("inline;filename=" + fileName);
// 上傳文件
ossClient.putObject(ossConfig.getBucketName(), fileName, inputStream, objectMetadata);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return this.ossConfig.getUrlPrefix() + fileName;
}
/**
* 獲取文件名
*
* @param muFile 文件
* @param isRetain 是否保留源文件名
* @return 返回文件名,以當前年月日作爲前綴路徑
*/
private String getFilePathName(MultipartFile file, boolean isRetain) {
String fileName = file.getOriginalFilename();
String name = fileName;
String fileSuffix = "";
if (fileName.indexOf('.') != -1) {
name = fileName.substring(0, fileName.indexOf('.'));
fileSuffix = fileName.substring(fileName.lastIndexOf("."));
}
LocalDate date = LocalDate.now();
StringBuilder filePathName = new StringBuilder(PATH);
filePathName.append(date.getYear());
filePathName.append("/");
filePathName.append(date.getMonthValue());
filePathName.append("/");
filePathName.append(date.getDayOfMonth());
filePathName.append("/");
// 添加隨機後綴
Random r = new Random();
int pix = r.ints(1, (100 + 1)).findFirst().getAsInt();
filePathName.append(System.currentTimeMillis());
filePathName.append("" + pix);
// 文件名超過32字符則截取
if (isRetain) {
filePathName.append("_");
if (name.length() >= 32) {
name = name.substring(0, 32);
}
filePathName.append(name);
}
filePathName.append(fileSuffix);
return filePathName.toString();
}
/**
* 處理文件擴展名
*
* @param fileSuffix
* @return
*/
public String getcontentType(String fileSuffix) {
if (fileSuffix.equalsIgnoreCase(".bmp")) {
return "image/bmp";
}
if (fileSuffix.equalsIgnoreCase(".gif")) {
return "image/gif";
}
if (fileSuffix.equalsIgnoreCase(".jpeg") || fileSuffix.equalsIgnoreCase(".jpg")
|| fileSuffix.equalsIgnoreCase(".png")) {
return "image/jpeg";
}
if (fileSuffix.equalsIgnoreCase(".html")) {
return "text/html";
}
if (fileSuffix.equalsIgnoreCase(".txt")) {
return "text/plain";
}
if (fileSuffix.equalsIgnoreCase(".vsd")) {
return "application/vnd.visio";
}
if (fileSuffix.equalsIgnoreCase(".pptx") || fileSuffix.equalsIgnoreCase(".ppt")) {
return "application/vnd.ms-powerpoint";
}
if (fileSuffix.equalsIgnoreCase(".docx") || fileSuffix.equalsIgnoreCase(".doc")) {
return "application/msword";
}
if (fileSuffix.equalsIgnoreCase(".xml")) {
return "text/xml";
}
// 默認返回類型
return "image/jpeg";
}
/**
* 創建存儲空間
*
* @param bucketName
* @return
*/
public boolean createBucket(String bucketName) {
try {
boolean flag = ossClient.doesBucketExist(bucketName);
if (!flag) {
ossClient.createBucket(bucketName);
// 設置存儲空間的權限爲公共讀,默認是私有。
ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 刪除存儲空間
*
* @param bucketName
* @return
*/
public boolean deleteBucket(String bucketName) {
try {
boolean flag = ossClient.doesBucketExist(bucketName);
if (flag) {
ossClient.deleteBucket(bucketName);
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 列舉所有的存儲空間(名稱)
*
* @return
*/
public List<String> listBuckets() {
List<Bucket> buckets = ossClient.listBuckets();
List<String> bucketNames = new ArrayList<>();
for (Bucket bucket : buckets) {
bucketNames.add(bucket.getName());
}
return bucketNames;
}
}
圖像工具類
需要添加依賴
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.11</version>
</dependency>
/**
* 圖像工具類
*/
public class ImageUtil {
// 壓縮率
private static final transient float IMAGE_RATIO = 0.1f;
// 壓縮最大寬度
private static final transient int IMAGE_WIDTH = 800;
// 水印透明度
private static float alpha = 0.3f;
// 水印文字字體
private static Font font = new Font("PingFang SC Regular", Font.PLAIN, 36);
// 水印文字顏色
private static Color color = new Color(111, 111, 111);
// 水印文字內容
private static final String text = "這是一個水印文本";
// 水印之間的間隔
private static final int XMOVE = 80;
// 水印之間的間隔
private static final int YMOVE = 80;
/**
* 壓縮圖像
*
* @param image
* @return
* @throws IOException
*/
public static BufferedImage compress(BufferedImage image) throws IOException {
Thumbnails.Builder<BufferedImage> imageBuilder = Thumbnails.of(image).outputQuality(IMAGE_RATIO);
if (image.getWidth() > IMAGE_WIDTH) {
return imageBuilder.width(IMAGE_WIDTH).asBufferedImage();
} else {
return imageBuilder.scale(1).asBufferedImage();
}
}
/**
* 圖像添加水印
*
* @param
* @return
*/
public static BufferedImage setWatermark(BufferedImage image) throws IOException {
return Thumbnails.of(image).outputQuality(IMAGE_RATIO).scale(1)
.watermark(Positions.BOTTOM_RIGHT, createWatermark(text, image.getWidth(), image.getHeight()), alpha)
.asBufferedImage();
}
/**
* 根據文件擴展名判斷文件是否圖片格式
*
* @return
*/
public static boolean isImage(String fileName) {
String[] imageExtension = new String[] { "jpeg", "jpg", "gif", "bmp", "png" };
for (String e : imageExtension)
if (getFileExtention(fileName).toLowerCase().equals(e))
return true;
return false;
}
/**
* 獲取文件後綴名稱
*
* @param fileName
* @return
*/
public static String getFileExtention(String fileName) {
String extension = fileName.substring(fileName.lastIndexOf(".") + 1);
return extension;
}
/**
* 根據圖片對象獲取對應InputStream
*
* @param image
* @param readImageFormat
* @return
* @throws IOException
*/
public static InputStream getInputStream(BufferedImage image, String readImageFormat) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(image, readImageFormat, os);
InputStream is = new ByteArrayInputStream(os.toByteArray());
os.close();
return is;
}
/**
* 創建水印圖片
*
* @param text 水印文字
* @param width 圖片寬
* @param height 圖片高
* @return
*/
public static BufferedImage createWatermark(String text, int width, int height) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 2.獲取圖片畫筆
Graphics2D g = image.createGraphics();
// ---------- 增加下面的代碼使得背景透明 -----------------
image = g.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
g.dispose();
g = image.createGraphics();
// ---------- 背景透明代碼結束 -----------------
// 6、處理文字
AttributedString ats = new AttributedString(text);
ats.addAttribute(TextAttribute.FONT, font, 0, text.length());
// 7、設置對線段的鋸齒狀邊緣處理
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 8、設置水印旋轉
g.rotate(Math.toRadians(-30));
// 9、設置水印文字顏色
g.setColor(color);
// 10、設置水印文字Font
g.setFont(font);
// 11、設置水印文字透明度
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
/**
* 水印鋪滿圖片 計算水印位置
*/
int x = -width / 2;
int y = -height / 2;
int[] arr = getWidthAndHeight(text, font);
int markWidth = arr[0];// 字體長度
int markHeight = arr[1];// 字體高度
// 循環添加水印
while (x < width * 1.5) {
y = -height / 2;
while (y < height * 1.5) {
g.drawString(text, x, y);
y += markHeight + YMOVE;
}
x += markWidth + XMOVE;
}
// 13、釋放資源
g.dispose();
return image;
}
/**
* 計算字體寬度及高度
*
* @param text
* @param font
* @return
*/
private static int[] getWidthAndHeight(String text, Font font) {
Rectangle2D r = font.getStringBounds(text,
new FontRenderContext(AffineTransform.getScaleInstance(1, 1), false, false));
int unitHeight = (int) Math.floor(r.getHeight());//
// 獲取整個str用了font樣式的寬度這裏用四捨五入後+1保證寬度絕對能容納這個字符串作爲圖片的寬度
int width = (int) Math.round(r.getWidth()) + 1;
// 把單個字符的高度+3保證高度絕對能容納字符串作爲圖片的高度
int height = unitHeight + 3;
return new int[] { width, height };
}
}
代碼託管:springboot_oss