JAVA集成阿里云OSS对象存储

超、凢脫俗 2023-09-23 19:43 160阅读 0赞

JAVA集成阿里云OSS对象存储

  • 1 : 配置集成
    • 1.1、对象存储OSS
  • 2 : 代码配置
    • 2.1、说明
    • 2.2、配置文件
    • 2.3、加载配置文件代码
    • 2.4、封装统一的DTO
    • 2.5、OSS上传Controller
    • 2.6、OSS上传Service
    • 2.7、OSS上传ServiceImpl
  • 3 : 测试
    • 3.1、文件上传
    • 3.2、文件迁移
  • 4 : 总结
    • 4.1、参考资料
    • 4.2、注意事项
    • 4.3、源码链接

1 : 配置集成

1.1、对象存储OSS

这就不用说了,从阿里云平台花钱买OSS对象存储就行了。
然后在bucket列表中创建自己使用的bucket即可。
比如创建:oss-test
然后从概览中找到 Access Key
在这里插入图片描述
在这里插入图片描述
然后就可以看到对应的id和key了。
在这里插入图片描述

这个时候,就有了四个参数
1、access-key:就是刚才的 AccessKey ID
2、secret-key:页面点击‘查看 Secret’就能看的到了
3、bucket:新建的桶的名称oss-test
4、endpoint:页面截图,选择自己的桶就能看到对应的endpoint

2 : 代码配置

2.1、说明

本篇代码,主要做的两件事:
1、文件上传至OSS
2、对服务器磁盘文件转存至OSS

2.2、配置文件

在application.yml中增加配置

  1. #阿里云oss
  2. alibaba:
  3. cloud:
  4. access-key: **********
  5. secret-key: **********
  6. oss:
  7. endpoint: oss********.aliyuncs.com
  8. bucket: *******
  9. config:
  10. # oss开关 0:关 1:开
  11. oss-switch: 1
  12. # oss 开始使用时间
  13. oss-date: 2022-01-01
  14. # oss 文件夹目录
  15. oss-directory: image,attach,video,audio,template
  16. # oss title
  17. oss-title: https://
  18. # oss business directory
  19. business-directory: user,org

非常非常重要在配置的时候上面4个重要参数的位置,以及名称一定不能修改,否则会报找不到endPoint的错

2.3、加载配置文件代码

新建 YmlConfigBean.java

  1. package com.upload.bean;
  2. import org.springframework.stereotype.Component;
  3. import java.util.List;
  4. /**
  5. * 配置文件参数读取
  6. */
  7. @Component
  8. public class YmlConfigBean {
  9. // 阿里云OSS 申请的 key id
  10. public volatile static String OSS_KEY_ID;
  11. // 阿里云OSS 申请的 key secret
  12. public volatile static String OSS_KEY_SECRET;
  13. // 阿里云OSS endpoint
  14. public volatile static String OSS_ENDPOINT;
  15. // 阿里云OSS bucket
  16. public volatile static String OSS_BUCKET;
  17. // 阿里云OSS 开关 0 关 1 开
  18. public volatile static String OSS_SWITCH;
  19. // 阿里云OSS 开始使用时间
  20. public volatile static String OSS_DATE;
  21. // 阿里云OSS 文件夹目录
  22. public volatile static List<String> OSS_DIRECTORY;
  23. // 阿里云OSS 开始使用时间
  24. public volatile static String OSS_TITLE;
  25. //业务文件类别
  26. public volatile static List<String> BUSINESS_DIRECTORY;
  27. }

新建 YmlConfigBeanMethod.java

  1. package com.upload.bean;
  2. import org.springframework.beans.factory.annotation.Value;
  3. import org.springframework.stereotype.Component;
  4. import java.util.List;
  5. @Component
  6. public class YmlConfigBeanMethod {
  7. @Value("#{'${alibaba.cloud.access-key}'}")
  8. private void setOssKeyId(String key) {
  9. YmlConfigBean.OSS_KEY_ID = key;
  10. }
  11. @Value("#{'${alibaba.cloud.secret-key}'}")
  12. private void setOssKeySecret(String key) {
  13. YmlConfigBean.OSS_KEY_SECRET = key;
  14. }
  15. @Value("#{'${alibaba.cloud.oss.endpoint}'}")
  16. private void setOssEndpoint(String key) {
  17. YmlConfigBean.OSS_ENDPOINT = key;
  18. }
  19. @Value("#{'${alibaba.cloud.oss.bucket}'}")
  20. private void setOssBucket(String key) {
  21. YmlConfigBean.OSS_BUCKET = key;
  22. }
  23. @Value("#{'${alibaba.config.oss-switch}'}")
  24. private void setOssSwitch(String key) {
  25. YmlConfigBean.OSS_SWITCH = key;
  26. }
  27. @Value("#{'${alibaba.config.oss-date}'}")
  28. private void setOssDate(String key) {
  29. YmlConfigBean.OSS_DATE = key;
  30. }
  31. @Value("#{'${alibaba.config.oss-directory}'.split(',')}")
  32. public void setOssDirectory(List<String> key){
  33. YmlConfigBean.OSS_DIRECTORY = key;
  34. }
  35. @Value("#{'${alibaba.config.oss-title}'}")
  36. private void setOssTitle(String key) {
  37. YmlConfigBean.OSS_TITLE = key;
  38. }
  39. @Value("#{'${alibaba.config.business-directory}'.split(',')}")
  40. public void setBusinessDirectory(List<String> key){
  41. YmlConfigBean.BUSINESS_DIRECTORY= key;
  42. }
  43. }

2.4、封装统一的DTO

建一个统一的DTO Result.java

  1. package com.upload.bean;
  2. /**
  3. * 统一的DTO
  4. */
  5. public class Result<T> {
  6. private boolean success = true;
  7. private Integer code = 0;
  8. private String msg;
  9. private T data;
  10. public boolean isSuccess() {
  11. return this.code == 0;
  12. }
  13. public void setSuccess(boolean success) {
  14. this.success = success;
  15. }
  16. public Integer getCode() {
  17. return code;
  18. }
  19. public void setCode(Integer code) {
  20. this.code = code;
  21. if(code != 0){
  22. success = false;
  23. }
  24. }
  25. public String getMsg() {
  26. return msg;
  27. }
  28. public void setMsg(String msg) {
  29. this.msg = msg;
  30. }
  31. public T getData() {
  32. return data;
  33. }
  34. public void setData(T data) {
  35. this.data = data;
  36. }
  37. public Result() {
  38. super();
  39. }
  40. public Result(boolean success) {
  41. this.success = success;
  42. if(success){
  43. code = 0;
  44. }
  45. }
  46. public Result(int code) {
  47. super();
  48. success = false;
  49. this.code = code;
  50. }
  51. public Result(String msg) {
  52. success = false;
  53. this.msg = msg;
  54. }
  55. public Result(boolean success, String msg) {
  56. super();
  57. this.success = success;
  58. this.msg = msg;
  59. }
  60. public Result(int code, String msg) {
  61. super();
  62. success = false;
  63. this.code = code;
  64. this.msg = msg;
  65. }
  66. /*设置返回为成功*/
  67. public static Result ok() {
  68. return new Result();
  69. }
  70. /*设置返回为成功,同时设置返回数据*/
  71. public Result<T> ok(T data) {
  72. Result<T> result = new Result<T>();
  73. result.setData(data);
  74. return result;
  75. }
  76. /*设置返回为失败,默认未知异常*/
  77. public static Result error() {
  78. return error(500, "未知异常,请联系管理员!");
  79. }
  80. /*设置返回为失败,同时设置异常信息*/
  81. public static Result error(String msg) {
  82. return error(500, msg);
  83. }
  84. /*设置返回为失败,同时设置code,msg*/
  85. public static Result error(Integer code,String msg) {
  86. Result result = new Result();
  87. result.setCode(code);
  88. result.setMsg(msg);
  89. return result;
  90. }
  91. public static Result<Object> error(Integer code,String msg,Object data) {
  92. Result<Object> result = new Result<Object>();
  93. result.setData(data);
  94. result.setCode(code);
  95. result.setMsg(msg);
  96. return result;
  97. }
  98. public static Result success(Object data) {
  99. Result<Object> result = new Result<Object>();
  100. result.setData(data);
  101. return result;
  102. }
  103. public static Result success(String msg) {
  104. Result result = new Result();
  105. result.setMsg(msg);
  106. return result;
  107. }
  108. }

常量配置好之后,方便后续直接使用配置文件的中内容。

2.5、OSS上传Controller

新建 OssUploadController.java

  1. package com.upload;
  2. import com.upload.service.IOssUploadService;
  3. import com.upload.bean.Result;
  4. import org.springframework.stereotype.Controller;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.bind.annotation.RequestParam;
  7. import org.springframework.web.bind.annotation.ResponseBody;
  8. import org.springframework.web.multipart.MultipartFile;
  9. import javax.annotation.Resource;
  10. import javax.servlet.http.HttpServletRequest;
  11. /**
  12. * <p>
  13. * 阿里云OSS 前端控制器
  14. * </p>
  15. */
  16. @Controller
  17. public class OssUploadController {
  18. @Resource
  19. private IOssUploadService ossUploadService;
  20. /**
  21. * OSS文件上传
  22. */
  23. @RequestMapping("/uploadOss")
  24. @ResponseBody
  25. public Result upload(@RequestParam String ossDirectory, @RequestParam String businessDirectory,
  26. @RequestParam MultipartFile file, HttpServletRequest request) {
  27. return ossUploadService.upload(ossDirectory, businessDirectory, file, request);
  28. }
  29. /**
  30. * OSS文件迁移
  31. */
  32. @RequestMapping("/migrationOss")
  33. @ResponseBody
  34. public Result doDataMigration(@RequestParam String ossDirectory, @RequestParam String businessDirectory) {
  35. return ossUploadService.doDataMigration(ossDirectory, businessDirectory, true);
  36. }
  37. }

2.6、OSS上传Service

新建 IOssUploadService.java

  1. package com.upload.service;
  2. import com.upload.bean.Result;
  3. import org.springframework.web.multipart.MultipartFile;
  4. import javax.servlet.http.HttpServletRequest;
  5. /**
  6. * <p>
  7. * 阿里云OSS 服务类
  8. */
  9. public interface IOssUploadService {
  10. Result upload(String ossDirectory, String businessDirectory, MultipartFile file, HttpServletRequest request);
  11. Result doDataMigration(String ossDirectory, String businessDirectory, boolean threadFlag);
  12. }

2.7、OSS上传ServiceImpl

新建 OssUploadServiceImpl.java

  1. package com.upload.service.impl;
  2. import com.aliyun.oss.OSS;
  3. import com.aliyun.oss.OSSClientBuilder;
  4. import com.aliyun.oss.model.CannedAccessControlList;
  5. import com.upload.bean.Result;
  6. import com.upload.bean.YmlConfigBean;
  7. import com.upload.entity.FileUpload;
  8. import com.upload.service.IOssUploadService;
  9. import lombok.extern.slf4j.Slf4j;
  10. import org.apache.commons.codec.digest.DigestUtils;
  11. import org.apache.commons.lang.time.DateFormatUtils;
  12. import org.apache.logging.log4j.util.Strings;
  13. import org.springframework.stereotype.Service;
  14. import org.springframework.web.multipart.MultipartFile;
  15. import javax.servlet.http.HttpServletRequest;
  16. import java.io.File;
  17. import java.io.FileInputStream;
  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.security.SecureRandom;
  21. import java.util.ArrayList;
  22. import java.util.Date;
  23. import java.util.List;
  24. @Slf4j
  25. @Service
  26. public class OssUploadServiceImpl implements IOssUploadService {
  27. // @Resource
  28. // private UploadMapper uploadMapper;
  29. private final static String ROOT_PATH = "/data/file/";
  30. /**
  31. * 对外提供的文件上传
  32. * @param ossDirectory OSS所属模块
  33. * @param fileDir 文件所属模块
  34. * @param file 文件
  35. * @param request 请求
  36. */
  37. @Override
  38. public Result upload(String ossDirectory, String fileDir, MultipartFile file, HttpServletRequest request) {
  39. if (file.isEmpty()) {
  40. return Result.error("不能上传空文件");
  41. }
  42. String originalFilename = file.getOriginalFilename();
  43. Long fileBytes = file.getSize();
  44. if (Strings.isBlank(originalFilename) || 0L == fileBytes) {
  45. return Result.error("不能上传空文件");
  46. }
  47. if(Strings.isBlank(ossDirectory)){
  48. ossDirectory = "image";
  49. } else {
  50. if (!YmlConfigBean.OSS_DIRECTORY.contains(ossDirectory.startsWith("/") ? ossDirectory.substring(1) : ossDirectory)) {
  51. log.info("文件上传目录非法,非法目录:"+ ossDirectory);
  52. return Result.error("文件上传目录非法,请更改文件上传目录");
  53. }
  54. }
  55. if (!YmlConfigBean.BUSINESS_DIRECTORY.contains(fileDir.startsWith("/") ? fileDir.substring(1) : fileDir)) {
  56. log.info("文件上传目录非法,非法目录:"+ fileDir);
  57. return Result.error("文件上传目录非法,请更改文件上传目录");
  58. }
  59. String fileMd5;
  60. try {
  61. fileMd5 = DigestUtils.sha256Hex(file.getInputStream());
  62. } catch (Exception e) {
  63. log.error(file.getOriginalFilename() + "验证文件md5失败",e);
  64. return Result.error("当前验证文件md5失败");
  65. }
  66. Result<String> result = new Result<>();
  67. String uploadUrl = null;
  68. String newName = null;
  69. OSS ossClient = getOSSClient();
  70. try {
  71. //获取上传文件流
  72. InputStream inputStream = file.getInputStream();
  73. //构建日期路径:/2019/02/26/文件名
  74. String fileType = originalFilename.split("\\.")[originalFilename.split("\\.").length - 1];
  75. newName = DateFormatUtils.format(new Date(), "yyMMddHHmmss") +
  76. (new SecureRandom().nextInt(100) + 100) + "." + fileType;
  77. String newFileDir = (fileDir.startsWith("/") ? fileDir.substring(1) : fileDir).trim().replace(" ","") +
  78. getDatePath();
  79. String fileUrl = ossDirectory + "/" + newFileDir + newName;
  80. // 上传文件 (上传文件流的形式)
  81. ossClient.putObject(YmlConfigBean.OSS_BUCKET, fileUrl, inputStream);
  82. //获取url地址
  83. uploadUrl = YmlConfigBean.OSS_TITLE + YmlConfigBean.OSS_BUCKET + "." + YmlConfigBean.OSS_ENDPOINT + "/" + fileUrl;
  84. } catch (IOException e) {
  85. log.error("上传OSS失败,文件名:" + originalFilename);
  86. throw new RuntimeException("上传OSS失败");
  87. } finally {
  88. // 关闭OSSClient。
  89. if (ossClient != null) {
  90. ossClient.shutdown();
  91. }
  92. }
  93. //文件上传实体属性赋值,数据入库
  94. try {
  95. log.info("原文件名称:" + originalFilename + "\n" + "文件MD5码:" + fileMd5 + "\n" + "新文件名称:" + newName + "\n" +
  96. "文件路径:" + uploadUrl + "\n" + "文件大小:" + fileBytes);
  97. // uploadMapper.doUploadInsert(null);
  98. } catch (Exception e) {
  99. log.error("上传文件,数据库记录失败", e);
  100. }
  101. result.setData(uploadUrl);
  102. return result;
  103. }
  104. /**
  105. * 文件迁移
  106. * @param ossDirectory 需要同步的OSS文件夹
  107. * @param businessDirectory 业务文件路径,以此查询表中数据
  108. * @param threadFlag 是否开启异步线程
  109. * @return Result
  110. */
  111. @Override
  112. public Result doDataMigration(String ossDirectory, String businessDirectory, boolean threadFlag){
  113. if(Strings.isBlank(businessDirectory)) return Result.error("路径不能为空");
  114. // List<FileUpload> fileUploads = uploadMapper.selectList();
  115. List<FileUpload> fileUploads = new ArrayList<>();
  116. if(null == fileUploads || fileUploads.size() == 0) return Result.error("暂无数据");
  117. if(threadFlag){
  118. Thread thread = new Thread(()->{
  119. doSonThread(fileUploads, ossDirectory);
  120. });
  121. thread.start();
  122. } else {
  123. doSonThread(fileUploads, ossDirectory);
  124. }
  125. return Result.success("文件迁移中,请稍后查看!");
  126. }
  127. private void doSonThread(List<FileUpload> fileUploads, String ossDirectory){
  128. if(Strings.isBlank(ossDirectory)) {
  129. ossDirectory = "image";
  130. }
  131. String filePath = null;
  132. for(FileUpload fileUpload : fileUploads){
  133. if(Strings.isBlank(fileUpload.getFilePath()) || Strings.isBlank(fileUpload.getFileName())){
  134. continue;
  135. }
  136. filePath = uploadFileAndDeleteByPath(ROOT_PATH + fileUpload.getFilePath(), fileUpload.getFileName(), ossDirectory);
  137. //更新新的路径入库
  138. fileUpload.setFilePathNew(filePath);
  139. // uploadMapper.doUploadUpdate(fileUpload);
  140. }
  141. }
  142. public static String uploadFileAndDeleteByPath(String path, String fileName, String ossDirectory) {
  143. File file = new File(path + File.separator + fileName);
  144. return uploadFileAndDeleteByFile(file, ossDirectory);
  145. }
  146. public static String uploadFileAndDeleteByFile(File file, String ossDirectory) {
  147. if (file.isDirectory()) return null;
  148. String filePath = null;
  149. Boolean deleteFlag = false;
  150. try {
  151. filePath = uploadHistory(ossDirectory, file.getPath(), file);
  152. deleteFlag = checkExists(filePath.substring(filePath.indexOf(YmlConfigBean.OSS_ENDPOINT) + 1 + YmlConfigBean.OSS_ENDPOINT.length(), filePath.length()));
  153. } catch (IOException e) {
  154. log.error("上传OSS失败,文件名:" + file.getName());
  155. throw new RuntimeException("上传OSS失败");
  156. }
  157. if(deleteFlag){
  158. file.delete();
  159. }
  160. return filePath;
  161. }
  162. public static String uploadHistory(String ossDirectory, String businessDirectory, File file) throws IOException{
  163. String filename = file.getName();
  164. OSS ossClient = getOSSClient();
  165. //获取上传文件流
  166. InputStream inputStream = new FileInputStream(file);
  167. //构建日期路径:/2019/02/26/文件名
  168. String fileType = filename.split("\\.")[filename.split("\\.").length - 1];
  169. String newName = filename.substring(0, filename.indexOf(".")) + "_" + (new SecureRandom().nextInt(1000) + 1000) + "." + fileType;
  170. String newFileDir = (businessDirectory.startsWith("/") ? businessDirectory.substring(1) : businessDirectory).trim().replace(" ","");
  171. String fileUrl = ossDirectory + "/" + newFileDir + newName;
  172. // 上传文件 (上传文件流的形式)
  173. ossClient.putObject(YmlConfigBean.OSS_BUCKET, fileUrl, inputStream);
  174. ossClient.shutdown();
  175. //获取url地址
  176. return YmlConfigBean.OSS_TITLE + YmlConfigBean.OSS_BUCKET + "." + YmlConfigBean.OSS_ENDPOINT + "/" + fileUrl;
  177. }
  178. public static boolean checkExists(String objectName) throws IOException{
  179. OSS ossClient = getOSSClient();
  180. Boolean flag = ossClient.doesObjectExist(YmlConfigBean.OSS_BUCKET, objectName);
  181. ossClient.shutdown();
  182. return flag;
  183. }
  184. public static OSS getOSSClient() {
  185. OSS ossClient = new OSSClientBuilder().build(YmlConfigBean.OSS_ENDPOINT, YmlConfigBean.OSS_KEY_ID, YmlConfigBean.OSS_KEY_SECRET);
  186. //判断oss实例是否存在:如果不存在则创建,如果存在则获取
  187. if (!ossClient.doesBucketExist(YmlConfigBean.OSS_BUCKET)) {
  188. //创建bucket
  189. ossClient.createBucket(YmlConfigBean.OSS_BUCKET);
  190. //设置oss实例的访问权限:公共读
  191. ossClient.setBucketAcl(YmlConfigBean.OSS_BUCKET, CannedAccessControlList.PublicRead);
  192. }
  193. return ossClient;
  194. }
  195. /*public static void uploadFileAndDelete(File file) {
  196. if (file.isDirectory()) {
  197. String[] fileList = file.list();
  198. if(null != fileList && fileList.length > 0){
  199. //递归删除目录中的子目录下
  200. File subFile = null;
  201. for(String str : fileList){
  202. subFile = new File(file, str);
  203. log.info(subFile.getPath());
  204. uploadFileAndDelete(subFile);
  205. }
  206. }
  207. }
  208. uploadFileAndDeleteByFile(file, null);
  209. }*/
  210. public static String getDatePath(){
  211. return "/" + DateFormatUtils.format(new Date(), "yyyy-MM-dd").replaceAll("-","/") + "/";
  212. }
  213. }

3 : 测试

3.1、文件上传

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在OSS上存储的路径为:OSS文件夹(image)+ 业务自定义文件夹名称(org)+ 年月日时间路径 + 重命名文件名称。

3.2、文件迁移

大致思路:
1、根据业务需要,查询数据库中的文件路径及名称,到服务器磁盘中查找该文件。
2、调用简易版的文件上传,上传至OSS中。
3、上传之后回调阿里自带方法doesObjectExist进行回溯校验,检查是否在OSS中存在。
4、如存在该文件,则删除服务器磁盘对应的文件即可。

因代码需根据业务数据来进行数据的查询等操作,可自行修改并验证。

4 : 总结

4.1、参考资料

参考资料:异常官网链接:https://help.aliyun.com/document_detail/32023.html

4.2、注意事项

注意事项在配置的时候上面4个重要参数的位置,以及名称一定不能修改,否则会报找不到endPoint的错

4.3、源码链接

CSDN资源链接:https://download.csdn.net/download/qq_38254635/87352781
百度网盘资源链接:https://pan.baidu.com/s/1kl6KIntr1qvcJPG642DWAA?pwd=fsj2
提取码: fsj2

有什么不对的还望指正,书写不易,觉得有帮助就点个赞吧!

发表评论

表情:
评论列表 (有 0 条评论,160人围观)

还没有评论,来说两句吧...

相关阅读

    相关 阿里存储OSS

    为了解决海量数据存储与弹性扩容,项目中我们采用云存储的解决方案- 阿里云OSS。 一 开通“对象存储OSS”服务 1 申请阿里云账号 2 实名认证 3 开