1. 程式人生 > 程式設計 >springboot操作阿里雲OSS實現檔案上傳,下載,刪除功能

springboot操作阿里雲OSS實現檔案上傳,下載,刪除功能

參考資料:Java操作阿里雲OSS操作官方文件

學會看文件,並實際運用也是一種習慣和技能

下面就來簡單入門一下,用當下比較熱門的Springboot 去操作阿里雲OSS檔案儲存。

1.需求

(沒踩過下面的坑的小夥伴可以直接跳過這一章節)

問題簡述

首先,我在之前自己做一些開源小專案案例中遇到一些檔案上傳下載的問題,比如在本機檔案上傳和下載都可以正常使用,通過將檔案上傳到Springboot專案的根目錄下,按日期分資料夾,檔案訪問也很方便,可以直接返回檔案相對路徑地址,並直接可以訪問。

問題

然而,這種方式存在弊端,因為當專案打包(jar包)部署阿里雲學生機後,出現類似io.NotFoundException...(No Such Directory)

的問題,,而如果打war包部署到tomcat則沒問題,可以正常使用,經過排查很久,找出問題所在:
因為jar打包封裝後是不能改變其內部目錄結構的,也就是說,按日期分類的檔案上傳資料夾,如果當需要建立新日期的資料夾的時候,是無法在jar包中新增資料夾的,這時候就會出現IO異常問題。而對於放在tomcat中的war包,當tomcat執行的時候會自動解壓war包,其在伺服器上是存在真實路徑的。

解決方案

  • 方案一:我在網上找了一種方法,是通過打完jar包部署後,給springboot專案static下的檔案上傳資料夾單獨分離出來(相當於是以相對路徑換絕對路徑),訪問的時候直接相當通過伺服器上和jar包同級目錄下新建一個檔案上傳資料夾。
  • 方案二:直接將檔案上傳到伺服器指定路徑下的檔案上傳位置,這種方式也相當於直接使用絕對路徑。
  • 方案三:在伺服器上使用FastDFS和Nginx搭建分散式檔案儲存,這種方式比較複雜,而且學生及本來記憶體和頻寬就小,在自己電腦的虛擬機器可以試試這種方案,還是挺好用的,學生伺服器就算了。
  • 方案四:就是直接將檔案上傳到阿里雲OSS檔案儲存系統上

2. 阿里雲OSS購買和配置

這個比較簡單,給大家推薦一篇博文自己瞭解下阿里雲oss購買和配置,也可以參考阿里雲OSS官方文件。

3. Springboot操作OSS

建立一個spring boot專案,pom檔案需要引入依賴:

pom.xml

<dependencies>
 <!--
 個人版本踩坑:
 不加這個依賴的話,當在配置類中
 使用@ConfigurationProperties(prefix = "aliyun")註解時,
 我這個版本的spring boot會提示有問題
 -->
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-configuration-processor</artifactId>
 <optional>true</optional>
 </dependency>
 <!-- swagger2 -->
 <dependency>
 <groupId>io.springfox</groupId>
 <artifactId>springfox-swagger2</artifactId>
 <version>2.9.2</version>
 </dependency>
 <!-- swagger ui -->
 <dependency>
 <groupId>io.springfox</groupId>
 <artifactId>springfox-swagger-ui</artifactId>
 <version>2.9.2</version>
 </dependency>
 <!-- thymeleaf 可不加,個人習慣性引入 -->
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-thymeleaf</artifactId>
 </dependency>
 <!-- web -->
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <!-- 熱部署,看個人習慣 -->
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-devtools</artifactId>
 <scope>runtime</scope>
 <optional>true</optional>
 </dependency>
 
 <!-- 小辣椒外掛,推薦使用,可以節省javaBean的setter/getter,還可以使用鏈式呼叫 -->
 <dependency>
 <groupId>org.projectlombok</groupId>
 <artifactId>lombok</artifactId>
 <optional>true</optional>
 </dependency>
 <!-- fastJson -->
 <dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>fastjson</artifactId>
 <version>1.2.62</version>
 </dependency>
 <!-- aliyun-oos -->
 <dependency>
 <groupId>com.aliyun.oss</groupId>
 <artifactId>aliyun-sdk-oss</artifactId>
 <version>2.8.3</version>
 </dependency>
 <dependency>
 <groupId>joda-time</groupId>
 <artifactId>joda-time</artifactId>
 <version>2.10.1</version>
 </dependency>
 <!-- apache-common-lang3 -->
 <dependency>
 <groupId>org.apache.commons</groupId>
 <artifactId>commons-lang3</artifactId>
 <version>3.8.1</version>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-test</artifactId>
 <scope>test</scope>
 <exclusions>
 <exclusion>
 <groupId>org.junit.vintage</groupId>
 <artifactId>junit-vintage-engine</artifactId>
 </exclusion>
 </exclusions>
 </dependency>
</dependencies>

包結構很簡單:

在這裡插入圖片描述

我們使用自己新增的application-aliyun-oss.properties配置檔案,去配置OSS相關資訊,之所以不在application.yml 中配置,看個人習慣了,因為自定義的配置屬性還是提出來配比較好,沒必要所有的都配到application.yml(properties)中去。

application-aliyun-oss.properties

# 檔案上傳大小限制
spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=1000MB

# 地域節點
aliyun.endPoint=oss-cn-beijing.aliyuncs.com
# Bucket 域名
aliyun.urlPrefix=http://csp-xxxx.oss-cn-beijing.aliyuncs.com/
# accessKey Id
aliyun.accessKeyId=LTAI4XXXXXXXzqD1saGFZ
# accessKey Secret
aliyun.accessKeySecret=2WjxNXXXXXXXX4f2bREc
# 你的Bucket名稱
aliyun.bucketName=csp-xxxx
# 目標資料夾
aliyun.fileHost=files

config包下的相關配置類

AliyunOssConfig.java

/**
 * @Auther: csp1999
 * @Date: 2020/10/31/13:33
 * @Description: 阿里雲 OSS 基本配置
 */
// 宣告配置類,放入Spring容器
@Configuration
// 指定配置檔案位置
@PropertySource(value = {"classpath:application-aliyun-oss.properties"})
// 指定配置檔案中自定義屬性字首
@ConfigurationProperties(prefix = "aliyun")
@Data// lombok
@Accessors(chain = true)// 開啟鏈式呼叫
public class AliyunOssConfig {
 private String endPoint;// 地域節點
 private String accessKeyId;
 private String accessKeySecret;
 private String bucketName;// OSS的Bucket名稱
 private String urlPrefix;// Bucket 域名
 private String fileHost;// 目標資料夾

 // 將OSS 客戶端交給Spring容器託管
 @Bean
 public OSS OSSClient() {
 return new OSSClient(endPoint,accessKeyId,accessKeySecret);
 }
}

Swagger2Config.java

/**
 * @Auther: csp1999
 * @Date: 2020/10/31/16:30
 * @Description: Swagger 配置類
 */
@Configuration
@EnableSwagger2// 開啟swagger2
public class Swagger2Config {

 @Bean
 public Docket webApiConfig() {

 return new Docket(DocumentationType.SWAGGER_2)
 .groupName("webApi")
 .apiInfo(webApiInfo())
 .select()
 .paths(Predicates.not(PathSelectors.regex("/error.*")))
 .build();
 }

 private ApiInfo webApiInfo() {
 return new ApiInfoBuilder()
 .title("SpringBoot整合OSS-API文件")
 .description("阿里雲OSS-檔案上傳下載測試")
 .version("1.0")
 .contact(new Contact("CSP","https://blog.csdn.net/weixin_43591980",""))
 .build();
 }
}

定義一個關於執行狀態結果的列舉類

/**
 * @Auther: csp1999
 * @Date: 2020/10/31/17:03
 * @Description: 狀態碼列舉類
 */
public enum StatusCode {
 SUCCESS("success",200),ERROR("error",500);
 private String msg;
 private Integer code;

 StatusCode(String msg,Integer code){
 this.msg = msg;
 this.code = code;
 }
 StatusCode(Integer code){
 this.code = code;
 }
 StatusCode(String msg){
 this.msg = msg;
 }
 public String getMsg() {
 return msg;
 }
 public void setMsg(String msg) {
 this.msg = msg;
 }
 public Integer getCode() {
 return code;
 }
 public void setCode(Integer code) {
 this.code = code;
 }
}

service層

在service使用ossClient操作阿里雲OSS,進行上傳、下載、刪除、檢視所有檔案等操作,同時可以將圖片的url進行入庫操作:

FileUploadService.java

/**
 * @Auther: csp1999
 * @Date: 2020/10/31/14:30
 * @Description: 檔案上傳Service (為節省文章中的程式碼篇幅,不再做介面實現類處理)
 */
@Service("fileUploadService")
public class FileUploadService {
 // 允許上傳檔案(圖片)的格式
 private static final String[] IMAGE_TYPE = new String[]{".bmp",".jpg",".jpeg",".gif",".png"};
 @Autowired
 private OSS ossClient;// 注入阿里雲oss檔案伺服器客戶端
 @Autowired
 private AliyunOssConfig aliyunOssConfig;// 注入阿里雲OSS基本配置類

 /*
 * 檔案上傳
 * 注:阿里雲OSS檔案上傳官方文件連結:https://help.aliyun.com/document_detail/84781.html?spm=a2c4g.11186623.6.749.11987a7dRYVSzn
 * @param: uploadFile
 * @return: string
 * @create: 2020/10/31 14:36
 * @author: csp1999
 */
 public String upload(MultipartFile uploadFile) {
 // 獲取oss的Bucket名稱
 String bucketName = aliyunOssConfig.getBucketName();
 // 獲取oss的地域節點
 String endpoint = aliyunOssConfig.getEndPoint();
 // 獲取oss的AccessKeySecret
 String accessKeySecret = aliyunOssConfig.getAccessKeySecret();
 // 獲取oss的AccessKeyId
 String accessKeyId = aliyunOssConfig.getAccessKeyId();
 // 獲取oss目標資料夾
 String filehost = aliyunOssConfig.getFileHost();
 // 返回圖片上傳後返回的url
 String returnImgeUrl = "";

 // 校驗圖片格式
 boolean isLegal = false;
 for (String type : IMAGE_TYPE) {
 if (StringUtils.endsWithIgnoreCase(uploadFile.getOriginalFilename(),type)) {
 isLegal = true;
 break;
 }
 }
 if (!isLegal) {// 如果圖片格式不合法
 return StatusCode.ERROR.getMsg();
 }
 // 獲取檔案原名稱
 String originalFilename = uploadFile.getOriginalFilename();
 // 獲取檔案型別
 String fileType = originalFilename.substring(originalFilename.lastIndexOf("."));
 // 新檔名稱
 String newFileName = UUID.randomUUID().toString() + fileType;
 // 構建日期路徑,例如:OSS目標資料夾/2020/10/31/檔名
 String filePath = new SimpleDateFormat("yyyy/MM/dd").format(new Date());
 // 檔案上傳的路徑地址
 String uploadImgeUrl = filehost + "/" + filePath + "/" + newFileName;

 // 獲取檔案輸入流
 InputStream inputStream = null;
 try {
 inputStream = uploadFile.getInputStream();
 } catch (IOException e) {
 e.printStackTrace();
 }
 /**
 * 下面兩行程式碼是重點坑:
 * 現在阿里雲OSS 預設圖片上傳ContentType是image/jpeg
 * 也就是說,獲取圖片連結後,圖片是下載連結,而並非在線瀏覽連結,
 * 因此,這裡在上傳的時候要解決ContentType的問題,將其改為image/jpg
 */
 ObjectMetadata meta = new ObjectMetadata();
 meta.setContentType("image/jpg");

 //檔案上傳至阿里雲OSS
 ossClient.putObject(bucketName,uploadImgeUrl,inputStream,meta);
 /**
 * 注意:在實際專案中,檔案上傳成功後,資料庫中儲存檔案地址
 */
 // 獲取檔案上傳後的圖片返回地址
 returnImgeUrl = "http://" + bucketName + "." + endpoint + "/" + uploadImgeUrl;

 return returnImgeUrl;
 }

 /*
 * 檔案下載
 * @param: fileName
 * @param: outputStream
 * @return: void
 * @create: 2020/10/31 16:19
 * @author: csp1999
 */
 public String download(String fileName,HttpServletResponse response) throws UnsupportedEncodingException {
// // 設定響應頭為下載
// response.setContentType("application/x-download");
// // 設定下載的檔名
// response.addHeader("Content-Disposition","attachment;fileName=" + fileName);
// response.setCharacterEncoding("UTF-8");
 // 檔名以附件的形式下載
 response.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(fileName,"UTF-8"));

 // 獲取oss的Bucket名稱
 String bucketName = aliyunOssConfig.getBucketName();
 // 獲取oss目標資料夾
 String filehost = aliyunOssConfig.getFileHost();
 // 日期目錄
 // 注意,這裡雖然寫成這種固定獲取日期目錄的形式,邏輯上確實存在問題,但是實際上,filePath的日期目錄應該是從資料庫查詢的
 String filePath = new DateTime().toString("yyyy/MM/dd");

 String fileKey = filehost + "/" + filePath + "/" + fileName;
 // ossObject包含檔案所在的儲存空間名稱、檔名稱、檔案元資訊以及一個輸入流。
 OSSObject ossObject = ossClient.getObject(bucketName,fileKey);
 try {
 // 讀取檔案內容。
 InputStream inputStream = ossObject.getObjectContent();
 BufferedInputStream in = new BufferedInputStream(inputStream);// 把輸入流放入快取流
 ServletOutputStream outputStream = response.getOutputStream();
 BufferedOutputStream out = new BufferedOutputStream(outputStream);// 把輸出流放入快取流
 byte[] buffer = new byte[1024];
 int len = 0;
 while ((len = in.read(buffer)) != -1) {
 out.write(buffer,len);
 }
 if (out != null) {
 out.flush();
 out.close();
 }
 if (in != null) {
 in.close();
 }
 return StatusCode.SUCCESS.getMsg();
 } catch (Exception e) {
 return StatusCode.ERROR.getMsg();
 }
 }

 /*
 * 檔案刪除
 * @param: objectName
 * @return: java.lang.String
 * @create: 2020/10/31 16:50
 * @author: csp1999
 */
 public String delete(String fileName) {
 // 獲取oss的Bucket名稱
 String bucketName = aliyunOssConfig.getBucketName();
 // 獲取oss的地域節點
 String endpoint = aliyunOssConfig.getEndPoint();
 // 獲取oss的AccessKeySecret
 String accessKeySecret = aliyunOssConfig.getAccessKeySecret();
 // 獲取oss的AccessKeyId
 String accessKeyId = aliyunOssConfig.getAccessKeyId();
 // 獲取oss目標資料夾
 String filehost = aliyunOssConfig.getFileHost();
 // 日期目錄
 // 注意,這裡雖然寫成這種固定獲取日期目錄的形式,邏輯上確實存在問題,但是實際上,filePath的日期目錄應該是從資料庫查詢的
 String filePath = new DateTime().toString("yyyy/MM/dd");

 try {
 /**
 * 注意:在實際專案中,不需要刪除OSS檔案伺服器中的檔案,
 * 只需要刪除資料庫儲存的檔案路徑即可!
 */
 // 建議在方法中建立OSSClient 而不是使用@Bean注入,不然容易出現Connection pool shut down
 OSSClient ossClient = new OSSClient(endpoint,accessKeySecret);
 // 根據BucketName,filetName刪除檔案
 // 刪除目錄中的檔案,如果是最後一個檔案fileoath目錄會被刪除。
 String fileKey = filehost + "/" + filePath + "/" + fileName;
 ossClient.deleteObject(bucketName,fileKey);

 try {
 } finally {
 ossClient.shutdown();
 }
 System.out.println("檔案刪除!");
 return StatusCode.SUCCESS.getMsg();
 } catch (Exception e) {
 e.printStackTrace();
 return StatusCode.ERROR.getMsg();
 }
 }
}

controller層

controller提供測試介面

/**
 * @Auther: csp1999
 * @Date: 2020/10/31/16:40
 * @Description: OSS 檔案上傳controller
 */
@Api(description = "阿里雲OSS檔案上傳、下載、刪除API")
@RequestMapping("api/pri/file")
@RestController
public class OssFileController {
 @Autowired
 private FileUploadService fileUploadService;

 /*
 * 檔案上傳api
 * @param: file
 * @return: com.alibaba.fastjson.JSONObject
 * @create: 2020/10/31 17:35
 * @author: csp1999
 */
 @ApiOperation(value = "檔案上傳")
 @PostMapping("upload")
 public JSONObject upload(@RequestParam("file") MultipartFile file) {
 JSONObject jsonObject = new JSONObject();
 if (file != null) {
 String returnFileUrl = fileUploadService.upload(file);
 if (returnFileUrl.equals("error")) {
 jsonObject.put("error","檔案上傳失敗!");
 return jsonObject;
 }
 jsonObject.put("success","檔案上傳成功!");
 jsonObject.put("returnFileUrl",returnFileUrl);
 return jsonObject;
 } else {
 jsonObject.put("error","檔案上傳失敗!");
 return jsonObject;
 }
 }

 /*
 * 檔案下載api
 * @param: fileName
 * @param: response
 * @return: com.alibaba.fastjson.JSONObject
 * @create: 2020/10/31 17:35
 * @author: csp1999
 */
 @ApiOperation(value = "檔案下載")
 @GetMapping(value = "download/{fileName}")
 public JSONObject download(@PathVariable("fileName") String fileName,HttpServletResponse response) throws Exception {
 JSONObject jsonObject = new JSONObject();

 String status = fileUploadService.download(fileName,response);
 if (status.equals("error")) {
 jsonObject.put("error","檔案下載失敗!");
 return jsonObject;
 } else {
 jsonObject.put("success","檔案下載成功!");
 return jsonObject;
 }
 }

 /*
 * 檔案刪除api
 * @param: fileName
 * @return: com.alibaba.fastjson.JSONObject
 * @create: 2020/10/31 17:35
 * @author: csp1999
 */
 @ApiOperation(value = "檔案刪除")
 @GetMapping("/delete/{fileName}")
 public JSONObject DeleteFile(@PathVariable("fileName") String fileName) {
 JSONObject jsonObject = new JSONObject();

 String status = fileUploadService.delete(fileName);
 if (status.equals("error")) {
 jsonObject.put("error","檔案刪除失敗!");
 return jsonObject;
 } else {
 jsonObject.put("success","檔案刪除成功!");
 return jsonObject;
 }
 }
}

4.執行專案測試API介面

本機訪問:http://localhost:8083/swagger-ui.html

在這裡插入圖片描述

測試上傳:

在這裡插入圖片描述

結果如圖:

在這裡插入圖片描述

如果說明上傳成功,我們來看一下這個連結能不能訪問到:

在這裡插入圖片描述
在這裡插入圖片描述

上傳成功!

到此這篇關於springboot操作阿里雲OSS實現檔案上傳,下載,刪除功能的文章就介紹到這了,更多相關springboot檔案上傳下載刪除內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!