springboot操作阿里云OSS實現(xiàn)文件上傳,下載,刪除功能
參考資料:Java操作阿里云OSS操作官方文檔
學會看文檔,并實際運用也是一種習慣和技能
下面就來簡單入門一下,用當下比較熱門的Springboot 去操作阿里云OSS文件存儲。
1.需求
(沒踩過下面的坑的小伙伴可以直接跳過這一章節(jié))
問題簡述
首先,我在之前自己做一些開源小項目案例中遇到一些文件上傳下載的問題,比如在本機文件上傳和下載都可以正常使用,通過將文件上傳到Springboot項目的根目錄下,按日期分文件夾,文件訪問也很方便,可以直接返回文件相對路徑地址,并直接可以訪問。
問題
然而,這種方式存在弊端,因為當項目打包(jar包)部署阿里云學生機后,出現(xiàn)類似io.NotFoundException...(No Such Directory)
的問題,,而如果打war包部署到tomcat則沒問題,可以正常使用,經(jīng)過排查很久,找出問題所在:
因為jar打包封裝后是不能改變其內部目錄結構的,也就是說,按日期分類的文件上傳文件夾,如果當需要創(chuàng)建新日期的文件夾的時候,是無法在jar包中新增文件夾的,這時候就會出現(xiàn)IO異常問題。而對于放在tomcat中的war包,當tomcat運行的時候會自動解壓war包,其在服務器上是存在真實路徑的。
解決方案
- 方案一:我在網(wǎng)上找了一種方法,是通過打完jar包部署后,給springboot項目static下的文件上傳文件夾單獨分離出來(相當于是以相對路徑換絕對路徑),訪問的時候直接相當通過服務器上和jar包同級目錄下新建一個文件上傳文件夾。
- 方案二:直接將文件上傳到服務器指定路徑下的文件上傳位置,這種方式也相當于直接使用絕對路徑。
- 方案三:在服務器上使用FastDFS和Nginx搭建分布式文件存儲,這種方式比較復雜,而且學生及本來內存和帶寬就小,在自己電腦的虛擬機可以試試這種方案,還是挺好用的,學生服務器就算了。
- 方案四:就是直接將文件上傳到阿里云OSS文件存儲系統(tǒng)上
2. 阿里云OSS購買和配置
這個比較簡單,給大家推薦一篇博文自己了解下阿里云oss購買和配置,也可以參考阿里云OSS官方文檔。
3. Springboot操作OSS
創(chuàng)建一個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> <!-- 小辣椒插件,推薦使用,可以節(jié)省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 # 地域節(jié)點 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;// 地域節(jié)點 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(); } }
定義一個關于執(zhí)行狀態(tài)結果的枚舉類
/** * @Auther: csp1999 * @Date: 2020/10/31/17:03 * @Description: 狀態(tài)碼枚舉類 */ 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 (為節(jié)省文章中的代碼篇幅,不再做接口實現(xiàn)類處理) */ @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的地域節(jié)點 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(); } /** * 下面兩行代碼是重點坑: * 現(xiàn)在阿里云OSS 默認圖片上傳ContentType是image/jpeg * 也就是說,獲取圖片鏈接后,圖片是下載鏈接,而并非在線瀏覽鏈接, * 因此,這里在上傳的時候要解決ContentType的問題,將其改為image/jpg */ ObjectMetadata meta = new ObjectMetadata(); meta.setContentType("image/jpg"); //文件上傳至阿里云OSS ossClient.putObject(bucketName, uploadImgeUrl, inputStream, meta); /** * 注意:在實際項目中,文件上傳成功后,數(shù)據(jù)庫中存儲文件地址 */ // 獲取文件上傳后的圖片返回地址 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的日期目錄應該是從數(shù)據(jù)庫查詢的 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, 0, 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的地域節(jié)點 String endpoint = aliyunOssConfig.getEndPoint(); // 獲取oss的AccessKeySecret String accessKeySecret = aliyunOssConfig.getAccessKeySecret(); // 獲取oss的AccessKeyId String accessKeyId = aliyunOssConfig.getAccessKeyId(); // 獲取oss目標文件夾 String filehost = aliyunOssConfig.getFileHost(); // 日期目錄 // 注意,這里雖然寫成這種固定獲取日期目錄的形式,邏輯上確實存在問題,但是實際上,filePath的日期目錄應該是從數(shù)據(jù)庫查詢的 String filePath = new DateTime().toString("yyyy/MM/dd"); try { /** * 注意:在實際項目中,不需要刪除OSS文件服務器中的文件, * 只需要刪除數(shù)據(jù)庫存儲的文件路徑即可! */ // 建議在方法中創(chuàng)建OSSClient 而不是使用@Bean注入,不然容易出現(xiàn)Connection pool shut down OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret); // 根據(jù)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實現(xiàn)文件上傳,下載,刪除功能的文章就介紹到這了,更多相關springboot文件上傳下載刪除內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Springboot Retry組件@Recover失效問題解決方法
在使用springboot的retry模塊時,你是否出現(xiàn)過@Recover注解失效的問題呢?不用擔心,這篇文章就來告訴你解決@Recover失效的辦法,需要的小伙伴可以參考一下2021-11-11springboot?serviceImpl初始化注入對象實現(xiàn)方式
這篇文章主要介紹了springboot?serviceImpl初始化注入對象實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05Java Swing中的JButton、JComboBox、JList和JColorChooser組件使用案例
這篇文章主要介紹了Java Swing中的按鈕(JButton)、組合框(JComboBox)、下拉列表(JList)和顏色選擇器(JColorChooser)組件使用案例,需要的朋友可以參考下2014-10-10