通過redis來實現跨域上傳圖片到阿里 OSS並回顯進度條

 

OSS 官方給出的 java SDK 是這樣的,上傳的文件的時候使用監聽器來監聽上傳的進度,以實現上傳進度,官方示例如下:

public class PutObjectProgressListener implements ProgressListener {
    private long bytesWritten = 0;
    private long totalBytes = -1;
    private boolean succeed = false;
    @Override
    public void progressChanged(ProgressEvent progressEvent) {
        long bytes = progressEvent.getBytes();
        ProgressEventType eventType = progressEvent.getEventType();
        switch (eventType) {
        case TRANSFER_STARTED_EVENT:
            System.out.println("Start to upload......");
            break;
        case REQUEST_CONTENT_LENGTH_EVENT:
            this.totalBytes = bytes;
            System.out.println(this.totalBytes + " bytes in total will be uploaded to OSS");
            break;
        case REQUEST_BYTE_TRANSFER_EVENT:
            this.bytesWritten += bytes;
            if (this.totalBytes != -1) {
                int percent = (int)(this.bytesWritten * 100.0 / this.totalBytes);
                System.out.println(bytes + " bytes have been written at this time, upload progress: " + percent + "%(" + this.bytesWritten + "/" + this.totalBytes + ")");
            } else {
                System.out.println(bytes + " bytes have been written at this time, upload ratio: unknown" + "(" + this.bytesWritten + "/...)");
            }
            break;
        case TRANSFER_COMPLETED_EVENT:
            this.succeed = true;
            System.out.println("Succeed to upload, " + this.bytesWritten + " bytes have been transferred in total");
            break;
        case TRANSFER_FAILED_EVENT:
            System.out.println("Failed to upload, " + this.bytesWritten + " bytes have been transferred");
            break;
        default:
            break;
        }
    }
    public boolean isSucceed() {
        return succeed;
    }
    public static void main(String[] args) {
        // Endpoint以杭州爲例,其它Region請按實際情況填寫。
        String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
        // 阿里雲主賬號AccessKey擁有所有API的訪問權限,風險很高。強烈建議您創建並使用RAM賬號進行API訪問或日常運維,請登錄 https://ram.console.aliyun.com 創建RAM賬號。
        String accessKeyId = "<yourAccessKeyId>";
        String accessKeySecret = "<yourAccessKeySecret>";
        String bucketName = "<yourBucketName>";
        String objectName = "<yourObjectName>";
        // 創建OSSClient實例。
        OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
        try {
            // 帶進度條的上傳。
            ossClient.putObject(new PutObjectRequest(bucketName, objectName, new FileInputStream("<yourLocalFile>")).
                    <PutObjectRequest>withProgressListener(new PutObjectProgressListener()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 關閉OSSClient。
        ossClient.shutdown();
    }
}

  引用到spring boot 中的項目中之後的代碼:

1.文件上傳及監聽器:

package com.mamahao.ebiz.pos.web.oss;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.event.ProgressEvent;
import com.aliyun.oss.event.ProgressEventType;
import com.aliyun.oss.event.ProgressListener;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.google.common.collect.Lists;
import com.mamahao.data.framework.redis.service.RedisService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.support.DefaultMultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 *  2018/7/25.
 */
public class OssUploadUtil {
    private static Logger logger = LoggerFactory.getLogger(OssUploadUtil.class);




    private static final String accessKeyId = OSSProperties.getAccessKey();
    private static final String accessKeySecret =OSSProperties.getSecretKey();
    private static final String endpoint =OSSProperties.getEndpoint();
    private static final int seconds = OSSProperties.getSeconds();

    /**
     * 帶進度的上傳
     *
     * @param request
     * @return List<String> 返回的文件地址集合
     * @throws Exception
     */
    public static List<String> uploadPicWithProgress(HttpServletRequest request, String bucketName, String ossKey,String  redisKey,RedisService redisService) throws Exception {
        //每次調用都需要實例化一次,實例化的OSSClient shoutDown 一次就不行了
        OSSClient   oSSClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
        DefaultMultipartHttpServletRequest req = (DefaultMultipartHttpServletRequest) request;
        request.setCharacterEncoding("UTF-8");
        Map<String, MultipartFile> files = req.getFileMap();
        int i = 0;
        List<String> urlList = Lists.newArrayList();
        for (String key : files.keySet()) {
            String suffix = "";
            MultipartFile file = files.get(key);

            /* MultipartFile轉File  */
            File f = null;
            try {
                f = File.createTempFile("tmpFile", null);
                file.transferTo(f);
                f.deleteOnExit();
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (file.getOriginalFilename().contains(".")) {
                suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
            }
            String fileName = System.currentTimeMillis() + i + suffix;
            i++;
            try {
                PutObjectResult putObjectResult = oSSClient.putObject(new PutObjectRequest(bucketName, ossKey + fileName, f).
                        <PutObjectRequest>withProgressListener(new PutObjectProgressListener(redisKey,redisService)));
                URL imgUrl =  oSSClient.generatePresignedUrl(bucketName,ossKey + fileName,new Date());
             String url =  imgUrl.getAuthority()+imgUrl.getPath();
             logger.debug(url);
             urlList.add(url);
            } catch (OSSException oe) {
                logger.error("Caught an OSSException, which means your request made it to OSS, "
                        + "but was rejected with an error response for some reason.Error Message: " + oe.getErrorCode()
                        + "Error Code:" + oe.getErrorCode() + "Request ID:" + oe.getRequestId() + "Host ID:" + oe.getHostId(), oe);
                throw new OSSException(oe.getErrorMessage(), oe);
            } catch (ClientException ce) {
                logger.error("Caught an ClientException, which means the client encountered "
                        + "a serious internal problem while trying to communicate with OSS, "
                        + "such as not being able to access the network.Error Message:" + ce.getMessage(), ce);
                throw new ClientException(ce);
            } finally {
                oSSClient.shutdown();
            }
        }
        return urlList;
    }

    public static class PutObjectProgressListener implements ProgressListener {
        private String redisKey;
        private RedisService redisService;
        private long bytesWritten = 0;
        private long totalBytes = -1;
        private boolean succeed = false;
        private int percent = 0;

        //構造方法中加入redis
        public PutObjectProgressListener() {
        }
        public PutObjectProgressListener(String redisKey,RedisService  redisService) {
            this.redisKey = redisKey;
            this.redisService = redisService;
            //首次添加redis值
            redisService.set(redisKey,percent);
        }

        @Override
        public void progressChanged(ProgressEvent progressEvent) {
            long bytes = progressEvent.getBytes();
            ProgressEventType eventType = progressEvent.getEventType();
            switch (eventType) {
                case TRANSFER_STARTED_EVENT:
                    logger.info("Start to upload......");
                    break;
                case REQUEST_CONTENT_LENGTH_EVENT:
                    this.totalBytes = bytes;
                    logger.info(this.totalBytes + " bytes in total will be uploaded to OSS");
                    break;
                case REQUEST_BYTE_TRANSFER_EVENT:
                    this.bytesWritten += bytes;
                    if (this.totalBytes != -1) {
                        percent = (int) (this.bytesWritten*100 / this.totalBytes);
                        //將進度percent放入redis中
                        redisService.set(redisKey,percent);
                        logger.info(bytes + " bytes have been written at this time, upload progress: " + percent + "%(" + this.bytesWritten + "/" + this.totalBytes + ")");
                    } else {
                        logger.info(bytes + " bytes have been written at this time, upload ratio: unknown" + "(" + this.bytesWritten + "/...)");
                    }
                    break;
                case TRANSFER_COMPLETED_EVENT:
                    this.succeed = true;
                    logger.info("Succeed to upload, " + this.bytesWritten + " bytes have been transferred in total");
                    break;
                case TRANSFER_FAILED_EVENT:
                    logger.info("Failed to upload, " + this.bytesWritten + " bytes have been transferred");
                    break;
                default:
                    break;
            }
        }

        public boolean isSucceed() {
            return succeed;
        }
    }
}

controller層:

package com.mamahao.ebiz.pos.web.controller.fileUpload;

import com.aliyun.openservices.shade.com.alibaba.rocketmq.shade.com.alibaba.fastjson.JSONObject;
import com.mamahao.data.framework.redis.service.RedisService;
import com.mamahao.ebiz.pos.web.controller.BaseController;
import com.mamahao.ebiz.pos.web.oss.OssUploadUtil;
import com.mamahao.feign.extension.vo.response.ErrorResponse;
import com.mamahao.feign.extension.vo.response.SuccessResponse;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.InputStream;
import java.util.List;
import java.util.Objects;


@RestController
@RequestMapping("/V1/upos/oss/")
public class FileUploadController extends BaseController {
    private static Logger logger = LoggerFactory.getLogger(FileUploadController.class);

    @Autowired
    private RedisService redisService;

    /**
     * 阿里雲圖片BUCKET
     */
    private final String GOODS_PIC_BUCKET_NAME="goods-item-images";
    /**
     * 阿里雲圖片默認key
     */
    private final String OSS_KEY="uCan-goods";
    /**
     * 用於存放的上傳進度的,redis key 前綴
     */
    private final String U_POS_GOODS_PIC="U_POS_GOODS_PIC:";

    /**
     * 上傳文件
     * @param request
     * @return
     * @throws Exception
     */
    @RequestMapping("upload.do")
    @ResponseBody
    public Object uploadSection(HttpServletRequest request) throws Exception {
        String redisKey = request.getHeader("ucanPicId");
        if(StringUtils.isEmpty(redisKey)){
                return ErrorResponse.newInstance("上傳uCanPicId不能爲空!!");
        }

        //上傳進度同時_往redis中存進度
        List<String> urlList=  OssUploadUtil.uploadPicWithProgress(request,GOODS_PIC_BUCKET_NAME,OSS_KEY,U_POS_GOODS_PIC+redisKey,redisService);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("url","http://"+urlList.get(0));
        return SuccessResponse.newInstance(jsonObject);
    }

    /**
     * 獲取實時長傳進度
     * @return
     */
    @RequestMapping ("percent.do")
    public Object getUploadPercent(@RequestBody JSONObject jsonObject){
        String uCanPicId = Objects.isNull(jsonObject.get("ucanPicId")) ? "":jsonObject.get("ucanPicId").toString();
        if(StringUtils.isEmpty(uCanPicId)){
            return ErrorResponse.newInstance("上傳uCanPicId不能爲空!!");
        }
        String redisKey = U_POS_GOODS_PIC+uCanPicId;

        int percent = redisService.get(redisKey) == null ? 0: Integer.valueOf(redisService.get(redisKey).toString()).intValue();

        //避免死循環請求,每次請求,如果上傳進度是0,則加一,當連續調用20次依然爲0的時候則返回異常
        String countKey = redisKey+"_COUNT";
        if(percent == 0){
            redisService.incrementBy(countKey,1L);
        }
        int count = redisService.get(countKey) == null ? 0: Integer.valueOf(redisService.get(countKey).toString()).intValue();
        if(count > 20){
            return ErrorResponse.newInstance("上傳異常,進度爲0!!!");
        }
        if(percent >= 100){
            //上傳進度到100的時候——————刪除該redis值
            redisService.delete(redisKey);
            //上傳進度到100的時候——————刪除該redis值
            redisService.delete(countKey);
        }
        JSONObject result = new JSONObject();
        result.put("percent",percent);
        return SuccessResponse.newInstance(result);
    }
}

所需要的maven依賴:

        <!--OSS 阿里對象存儲 SDK-->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>2.8.3</version>
        </dependency>
        <!--文件上傳-->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3</version>
        </dependency>

補充:在使用spring boot 上傳文件的時候,還需要配置 MultipartResolver,如果是其他項目則可以在xml裏配置:

   

<!-- 定義文件上傳解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 設定默認編碼 -->
    <property name="defaultEncoding" value="UTF-8"></property>
    <!-- 設定文件上傳的最大值爲5MB,5*1024*1024 -->
    <property name="maxUploadSize" value="5242880"></property>
    <!-- 設定文件上傳時寫入內存的最大值,如果小於這個參數不會生成臨時文件,默認爲10240 -->
    <property name="maxInMemorySize" value="40960"></property>
    <!-- 上傳文件的臨時路徑 -->
    <property name="uploadTempDir" value="fileUpload/temp"></property>
    <!-- 延遲文件解析 -->
    <property name="resolveLazily" value="true"/>
</bean>

 如果是spring boot 則可以通過下面這種方式注入,兩者本質是一樣的,只是方式不同:

package com.mamahao.ebiz.pos.web.config;

import org.springframework.stereotype.Component;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;


@Component("multipartResolver")
public class MultipartResolver  extends CommonsMultipartResolver {

    private int maxUploadSize = 104857600;

    private int maxInMemorySize = 4096;

    public int getMaxUploadSize() {
        return maxUploadSize;
    }

    public void setMaxUploadSize(int maxUploadSize) {
        this.maxUploadSize = maxUploadSize;
    }

    public int getMaxInMemorySize() {
        return maxInMemorySize;
    }

    @Override
    public void setMaxInMemorySize(int maxInMemorySize) {
        this.maxInMemorySize = maxInMemorySize;
    }
}

 補充:

如果有需要可以看一下這位兄弟的寫的,我們知道一個用戶登錄的時候的請求是隻有一個唯一的sessionId的,如果是前後端分離的情況下再使用 session 的方式去保存和記錄對應的 百分比,那麼從用戶請求 -----> 到前端服務-----> 後端服務 ,跨域請求的情況下就會導致同一個用戶的請求最終傳的後端的時候 session 沒有保證是唯一的了,所以在這裏我使用redis來保存進度,key值是前端傳給我的,爲了保證key值的唯一性,可以使用用戶登錄之後的cookies加上當前的時間戳來生成,最終在改張圖片上傳完成之後再將該key刪掉就ok了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章