Springboot 遷移及上傳檔案至阿里雲OSS

艾神一不小心發表於2018-09-26

摘要:

本文介紹GitClub小程式後端的伺服器檔案的遷移以及更新圖片上傳至阿里雲OSS儲存伺服器,如果不瞭解GitClub小程式的朋友可以看下我的上篇文章關於Android開源庫分享平臺,(GitClub)微信小程式的開發體驗,在此特別糾正下,當前版本暫時只有Android的開源庫分享,後續會增加Java、iOS、前端等不同語言的分類,感興趣的可以去關注下我們的小程式,原始碼地址:GitClub。鋪墊結束,請使用掃描這個二維碼登陸GitClub小程式參觀。

Geek Reader小程式二維碼

一、匯入依賴包,在pox.xml中加入

<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>2.8.3</version>
</dependency>
複製程式碼

二、配置OSS的引數,建立類OSSConfig

1、新增oss.properties配置檔案,內容如下:
#阿里雲OSS配置
#原伺服器地址
bucketUrl = https://bucketName.oss-cn-shenzhen.aliyuncs.com
#自定義解析後伺服器地址
baseUrl = https://xxx.502tech.com
#可以選擇其他的地址
endpoint = https://oss-cn-qingdao.aliyuncs.com
#已經在控制檯建立的bucket
bucketName = bucketName
#你上傳檔案的儲存路徑,如果bucket中不存在則建立(其實原理並不是資料夾,只是檔名,詳情請先閱讀官方文件)
picLocation = GitClub/image/
#相應的id和key值,請填寫你具體的值,這裡不方便展示我自己的。
accessKeyId = 阿里雲OSS的accessKeyId
accessKeySecret = 阿里雲OSS的accessKeySecret

2、建立類OSSConfig
public class OSSConfig {

    private  String bucketUrl;  		//原圖片伺服器地址
    private  String baseUrl;  		//自定義解析後的圖片伺服器地址
    private  String endpoint;  		//連線區域地址
    private  String accessKeyId;  	//連線keyId
    private  String accessKeySecret;    //連線祕鑰
    private  String bucketName;  	//需要儲存的bucketName
    private  String picLocation;  	//圖片儲存路徑

    public OSSConfig() {
        try {
            this.bucketUrl = SystemConfig.getConfigResource("bucketUrl");
            this.baseUrl = SystemConfig.getConfigResource("baseUrl");
            this.endpoint = SystemConfig.getConfigResource("endpoint");
            this.bucketName = SystemConfig.getConfigResource("bucketName");
            this.picLocation = SystemConfig.getConfigResource("picLocation");
            this.accessKeyId = SystemConfig.getConfigResource("accessKeyId");
            this.accessKeySecret = SystemConfig.getConfigResource("accessKeySecret");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    ...
    省略get、set方法
複製程式碼

三、建立OSS工具類OSSUploadUtil

private static OSSConfig config = null;

    /**
     *
     * @MethodName: uploadFile
     * @Description: OSS單檔案上傳
     * @param file
     * @param fileType 檔案字尾
     * @return String 檔案地址
     */
    public static String uploadFile(File file,String fileType){
        config = config == null ? new OSSConfig():config;
        //通過UUID生成檔名
        String fileName = config.getPicLocation()
                +UUID.randomUUID().toString().toUpperCase()
                .replace("-", "")
                +"."+fileType;
        return putFile(file,fileType,fileName);
    }

    /**
     *
     * @MethodName: updateFile
     * @Description: 更新檔案:只更新內容,不更新檔名和檔案地址。
     * 		(因為地址沒變,可能存在瀏覽器原資料快取,不能及時載入新資料,例如圖片更新,請注意)
     * @param file
     * @param fileType
     * @param oldUrl
     * @return String
     */
    public static String updateFile(File file,String fileType,String oldUrl){
        String fileName = getFileName(oldUrl);
        if(fileName==null) return null;
        return putFile(file,fileType,fileName);
    }

    /**
     *
     * @MethodName: replaceFile
     * @Description: 替換檔案:刪除原檔案並上傳新檔案,檔名和地址同時替換
     * 		解決原資料快取問題,只要更新了地址,就能重新載入資料)
     * @param file
     * @param fileType 檔案字尾
     * @param oldUrl 需要刪除的檔案地址
     * @return String 檔案地址
     */
    public static String replaceFile(File file,String fileType,String oldUrl){
        boolean flag = deleteFile(oldUrl);		//先刪除原檔案
        if(!flag){
            //更改檔案的過期時間,讓他到期自動刪除。
        }
        return uploadFile(file, fileType);
    }

    /**
     *
     * @MethodName: deleteFile
     * @Description: 單檔案刪除
     * @param fileUrl 需要刪除的檔案url
     * @return boolean 是否刪除成功
     */
    public static boolean deleteFile(String fileUrl){
        config = config == null ? new OSSConfig():config;

        String bucketName = OSSUploadUtil.getBucketName(fileUrl);		//根據url獲取bucketName
        String fileName = OSSUploadUtil.getFileName(fileUrl);			//根據url獲取fileName
        if(bucketName==null||fileName==null) return false;
        OSSClient ossClient = null;
        try {
            ossClient = new OSSClient(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
            GenericRequest request = new DeleteObjectsRequest(bucketName).withKey(fileName);
            ossClient.deleteObject(request);
        } catch (Exception oe) {
            oe.printStackTrace();
            return false;
        } finally {
            ossClient.shutdown();
        }
        return true;
    }

    /**
     *
     * @MethodName: batchDeleteFiles
     * @Description: 批量檔案刪除(較快):適用於相同endPoint和BucketName
     * @param fileUrls 需要刪除的檔案url集合
     * @return int 成功刪除的個數
     */
    public static int deleteFile(List<String> fileUrls){
        int deleteCount = 0;	//成功刪除的個數
        String bucketName = OSSUploadUtil.getBucketName(fileUrls.get(0));		//根據url獲取bucketName
        List<String> fileNames = OSSUploadUtil.getFileName(fileUrls);			//根據url獲取fileName
        if(bucketName==null||fileNames.size()<=0) return 0;
        OSSClient ossClient = null;
        try {
            ossClient = new OSSClient(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
            DeleteObjectsRequest request = new DeleteObjectsRequest(bucketName).withKeys(fileNames);
            DeleteObjectsResult result = ossClient.deleteObjects(request);
            deleteCount = result.getDeletedObjects().size();
        } catch (OSSException oe) {
            oe.printStackTrace();
            throw new RuntimeException("OSS服務異常:", oe);
        } catch (ClientException ce) {
            ce.printStackTrace();
            throw new RuntimeException("OSS客戶端異常:", ce);
        } finally {
            ossClient.shutdown();
        }
        return deleteCount;

    }

    /**
     *
     * @MethodName: batchDeleteFiles
     * @Description: 批量檔案刪除(較慢):適用於不同endPoint和BucketName
     * @param fileUrls 需要刪除的檔案url集合
     * @return int 成功刪除的個數
     */
    public static int deleteFiles(List<String> fileUrls){
        int count = 0;
        for (String url : fileUrls) {
            if(deleteFile(url)){
                count++;
            }
        }
        return count;
    }

    /**
     *
     * @MethodName: putFile
     * @Description: 上傳檔案
     * @param file
     * @param fileType
     * @param fileName
     * @return String
     */
    private static String putFile(File file, String fileType, String fileName){
        config = config==null?new OSSConfig():config;
        String url = null;		//預設null
        OSSClient ossClient = null;
        try {
            ossClient = new OSSClient(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
            InputStream input = new FileInputStream(file);
            ObjectMetadata meta = new ObjectMetadata();				// 建立上傳Object的Metadata
            meta.setContentType(OSSUploadUtil.contentType(fileType));		// 設定上傳內容型別
            meta.setCacheControl("no-cache");					// 被下載時網頁的快取行為
            PutObjectRequest request = new PutObjectRequest(config.getBucketName(), fileName,input,meta);			//建立上傳請求
            ossClient.putObject(request);
            Date expiration = new Date(new Date().getTime() + 3600L * 1000 * 24 * 365 * 10); // 設定URL過期時間為10年  3600L* 1000*24*365*10
            //上傳成功再返回的檔案路徑
            url = ossClient.generatePresignedUrl(config.getBucketName(), fileName, expiration)
                                                .toString()
                                                .replaceFirst(config.getBucketUrl(), config.getBaseUrl());
        } catch (OSSException | FileNotFoundException | ClientException oe) {
            oe.printStackTrace();
            return null;
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return url;
    }

    /**
     *
     * @MethodName: contentType
     * @Description: 獲取檔案型別
     * @param fileType
     * @return String
     */
    private static String contentType(String fileType){
        fileType = fileType.toLowerCase();
        String contentType = "";
        switch (fileType) {
            case "bmp":	contentType = "image/bmp";
                break;
            case "gif":	contentType = "image/gif";
                break;
            case "png":
            case "jpeg":
            case "jpg":	contentType = "image/jpeg";
                break;
            case "html":contentType = "text/html";
                break;
            case "txt":	contentType = "text/plain";
                break;
            case "vsd":	contentType = "application/vnd.visio";
                break;
            case "ppt":
            case "pptx":contentType = "application/vnd.ms-powerpoint";
                break;
            case "doc":
            case "docx":contentType = "application/msword";
                break;
            case "xml":contentType = "text/xml";
                break;
            case "mp4":contentType = "video/mp4";
                break;
            default: contentType = "application/octet-stream";
                break;
        }
        return contentType;
    }

    /**
     *
     * @MethodName: getBucketName
     * @Description: 根據url獲取bucketName
     * @param fileUrl 檔案url
     * @return String bucketName
     */
    private static String getBucketName(String fileUrl){
        String http = "http://";
        String https = "https://";
        int httpIndex = fileUrl.indexOf(http);
        int httpsIndex = fileUrl.indexOf(https);
        int startIndex  = 0;
        if(httpIndex==-1){
            if(httpsIndex==-1){
                return null;
            }else{
                startIndex = httpsIndex+https.length();
            }
        }else{
            startIndex = httpIndex+http.length();
        }
        int endIndex = fileUrl.indexOf(".oss-");
        return fileUrl.substring(startIndex, endIndex);
    }

    /**
     *
     * @MethodName: getFileName
     * @Description: 根據url獲取fileName
     * @param fileUrl 檔案url
     * @return String fileName
     */
    private static String getFileName(String fileUrl){
        String str = "aliyuncs.com/";
        int beginIndex = fileUrl.indexOf(str);
        if(beginIndex==-1) return null;
        return fileUrl.substring(beginIndex+str.length());
    }

    /**
     *
     * @MethodName: getFileName
     * @Description: 根據url獲取fileNames集合
     * @param fileUrls 檔案url
     * @return List<String>  fileName集合
     */
    private static List<String> getFileName(List<String> fileUrls){
        List<String> names = new ArrayList<>();
        for (String url : fileUrls) {
            names.add(getFileName(url));
        }
        return names;
    }

}
複製程式碼

四、測試

//把之前上傳到專案特定目錄下的邏輯修改成上傳至OSS
@PostMapping("/uploadArticleImg")
    public Result<Map<String, String>> uploadArticleImg(@RequestParam(value = "article_img") MultipartFile file) {
        if (file == null || file.isEmpty() || file.getSize() == 0) {
            return ResultUtils.error(ResultCode.UPLOAD_FILE_EMPTY);
        }
        if (file.getSize() > 10 * 1024 * 1024) {
            return ResultUtils.error(ResultCode.UPLOAD_FILE_LIMIT);
        }
        Map<String, String> map = new HashMap<>();
        String fileType = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")+1);
        //OSS單檔案上傳,返回上傳成功後的oss儲存伺服器中的url
        String fileName = OSSUploadUtil.uploadFile(FileUtils.multi2File(file), fileType);
        map.put(file.getName(), fileName);
        return ResultUtils.ok(map);
    }
複製程式碼

五、如何把資料庫中把之前已經上傳的檔案URL更新成OSS返回的URL

@PostMapping("/trasform2OSS")
    public Result<Boolean> trasformImg2OSS(){
        File path = null;
        try {
            path = new File(ResourceUtils.getURL("classpath:").getPath());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        if (!path.exists()) path = new File("");
        System.out.println("path:" + path.getAbsolutePath());
        //如果上傳目錄為/static/images/upload/,則可以如下獲取:
        File upload = new File(path.getAbsolutePath(), "static/images/upload/");
        if (!upload.exists()) upload.mkdirs();
        System.out.println("upload url:" + upload.getAbsolutePath());
        File[] files = upload.listFiles();

        //更新圖片的url到oss  並把最新oss的圖片地址儲存到資料庫
        List<Article> articles = articleRepository.findAll();
        articles.forEach(article -> {
            if (null != article){
                for (File file : files){
                    if(null != file){
                        if(null == article.getImg_url() || null == file.getName()){
                            continue;
                        }
                        if(article.getImg_url().contains(file.getName())){
                            //上傳到oss
                            String fileType = file.getName().substring(file.getName().lastIndexOf(".")+1);
                            String url = OSSUploadUtil.uploadFile(file, fileType);
                            logger.info("上傳到oss地址:"+url);
                            //更新到伺服器
                            article.setImg_url(url);
                            articleRepository.saveAndFlush(article);
                            //更新資料到引擎
                            articleSearchRepository.save(new ESArticle(article));
                        }
                    }
                }
            }
        });
        return ResultUtils.ok(true);
    }
複製程式碼

六、阿里雲OSS管理配置的一些坑

因為之前我們的圖片上傳至專案目錄中的指定路徑如:502tech.com/geekdaily/x… 現在需要全部替換成如:xxx.502tech.com/123.png 格式,注意:這裡xxx.502tech.com是我們的二級域名,上傳檔案至OSS之後預設會返回如:bucketName.oss-cn-shenzhen.aliyuncs.com/123.png 格式。需要我們手動在阿里雲OSS域名管理中去繫結域名,如果想要使用https,則需要申請SSL證照,這裡你可以申請一個免費的,可以自行百度,大概15分鐘會申請成功,然後需要在OSS的域名管理中新增SSL證照,這裡切記:下載nginx版本的證照,然後複製貼上對應的公鑰和私鑰。

相關文章