java oss 简单上传,分片上传(加进度条),(流式)下载文件

1,前端调用接口实现下载 (流式下载大文件可能比较慢!也可以用直接获取下载URL的方式)

@GetMapping(value = "/downloadFile")
    public void downloadFile(@RequestParam String ossPath, HttpServletResponse response) {
        BufferedInputStream input;
        OutputStream outputStream;
        OSS ossClient = OssUtil.getOssBean();
        try {
            OSSObject ossObject = ossClient.getObject(OssUtil.BUCKET_NAME, ossPath);
//            response.reset();
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/x-msdownload");
            response.addHeader("Content-Disposition",
                    "attachment;fileName=\"" + new String(ossPath.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8)+"\"");
//                    "attachment;filename="+new String(ossPath.getBytes("gb2312"), "ISO8859-1"));
            input = new BufferedInputStream(ossObject.getObjectContent());
            byte[] buffBytes = new byte[1024];
            outputStream = response.getOutputStream();
            int read;
            while ((read = input.read(buffBytes)) != -1) {
                outputStream.write(buffBytes, 0, read);
            }
            outputStream.flush();
            // 数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作。
            ossObject.close();
        } catch (OSSException | ClientException | IOException oe) {
            oe.printStackTrace();
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

2,简单上传接口,上传进度条可用官方api实现,这块不做实现。

@PassToken
    @ApiOperation("简单上传")
    @PostMapping(value = "/uploadFile")
    public RestResult<Object> saveFile(HttpServletRequest request) throws IOException {
        //得到上传文件夹名称
        String folderName = request.getParameter("folderName");
        Map<String, Object> map = new HashMap<>();
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        MultipartFile multipartFile = multipartRequest.getFile("file");
        // 获取文件名
        assert multipartFile != null;
        String fileName = multipartFile.getOriginalFilename();
        // 获取文件后缀名
        assert fileName != null;
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        // 最后上传生成的文件名
        String substring = fileName.substring(0, fileName.lastIndexOf("."));
        String previewName = substring + System.currentTimeMillis() + "" + new SecureRandom().nextInt(0x0400) + suffixName;
        // oss中的文件夹名
        String ossPreviewName = folderName + "/" + previewName;
        // 创建上传文件的元信息,可以通过文件元信息设置HTTP header(设置了才能通过返回的链接直接访问)。
        ObjectMetadata objectMetadata = new ObjectMetadata();
        //设置文件类型
        objectMetadata.setContentType(GetFileType.getContentType(suffixName));
        Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 50);
        //文件预览
        if ("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet".equals(objectMetadata.getContentType()) || "application/vnd.ms-powerpoint".equals(objectMetadata.getContentType()) || "application/msword".equals(objectMetadata.getContentType())) {
            OssUtil.getOssBeanInner().putObject("你自己的bucketName", ossPreviewName, new ByteArrayInputStream(multipartFile.getBytes()), objectMetadata);
            GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest("你自己的bucketName", ossPreviewName, HttpMethod.GET);
            req.setExpiration(expiration);
            req.setProcess(OssUtil.STYLE);
            map.put("previewUrl", OssUtil.getOssBeanOut().generatePresignedUrl(req));
        } else {
            OssUtil.getOssBeanInner().putObject("你自己的bucketName", ossPreviewName, new ByteArrayInputStream(multipartFile.getBytes()), objectMetadata);
            String previewUrl = OssUtil.getOssBeanOut().generatePresignedUrl("你自己的bucketName", ossPreviewName, expiration).toString();
            map.put("previewUrl", previewUrl);
        }
        map.put("fileName", fileName);
        map.put("ossPreviewName", ossPreviewName);
        map.put("fileType", suffixName);
        OssUtil.getOssBeanInner().shutdown();
        OssUtil.getOssBeanOut().shutdown();
        return RestResult.result(CommonResult.UPLOAD_SUCCESS, map);
    }

还有一种方式获取预览链接,但是这块要开通(IMM通智能媒体预览),以及要(绑定自有域名),在OSS管理控制台中需要配置(数据处理->文档处理->文档在线预览),就能够实现文件是否支持下载,复制,打印,还能添加水印,这种形式获取文件预览url的前提是(oss库中必须有)这个文件(推荐大家用这种形式来达到文件的管控数据安全)

public static String getWebOfficeUrl(String objName) {

        // 主要用于预览文件  绑定的自有域名
        String endpoint = "";
        // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
        // 填写Bucket名称,例如examplebucket。
        String bucketName = "";
        // 填写Object完整路径。Object完整路径中不能包含Bucket名称。

        ClientBuilderConfiguration cf = new ClientBuilderConfiguration();
        cf.setSupportCname(true);
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, "你的key", "你的秘钥", cf);

        try {
            // 这块就是管理控制台设置的文档水印,是否支持下载,打印。
            String style = "doc/preview,print_0,copy_0,export_0/watermark,text_6Im-6Ieq5rGH,size_40,t_15,rotate_0,color_#000000";
            // 指定签名URL过期时间。
            Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 50);
            GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(bucketName, objName, HttpMethod.GET);
            req.setExpiration(expiration);
            req.setProcess(style);
            return ossClient.generatePresignedUrl(req).toString();
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("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.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return "";

    }

3,分片上传,官方分片上传不支持进度条,我这块用的redis实现,也可用session ,大文件上传耗时前后端可能会断连导致没有上传成功的提示,所以这块最保险的做法还是websocket通信,实时告知前端进度

@PassToken
    @PostMapping(value = "/multipartUpload")
    public RestResult<Object> multipartUpload(HttpServletRequest request) throws Exception {
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        MultipartFile multipartFile = multipartRequest.getFile("file");
        assert multipartFile != null;
        String fileName = multipartFile.getOriginalFilename();
        assert fileName != null;
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        // 创建InitiateMultipartUploadRequest对象。
        InitiateMultipartUploadRequest iuRequest = new InitiateMultipartUploadRequest(OssUtil.BUCKET_NAME, "test/" + fileName);
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentType(GetFileType.getContentType(suffixName));
        //设置文件类型
        iuRequest.setObjectMetadata(objectMetadata);
        InitiateMultipartUploadResult upresult = OssUtil.getOssBeanInner().initiateMultipartUpload(iuRequest);
        // 返回uploadId,它是分片上传事件的唯一标识。您可以根据该uploadId发起相关的操作,例如取消分片上传、查询分片上传等。
        String uploadId = upresult.getUploadId();
        // partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。
        List<PartETag> partETags = new ArrayList<PartETag>();
        // 每个分片的大小,用于计算文件有多少个分片。单位为字节。
        final long partSize = 10 * 1024 * 1024L;
        long fileLength = multipartFile.getSize();
        int partCount = (int) (fileLength / partSize);
        System.err.println(partCount);
        if (fileLength % partSize != 0) {
            partCount++;
        }
        // 遍历分片上传。
        for (int i = 0; i < partCount; i++) {
            long startPos = i * partSize;
            long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
            InputStream inputStream = multipartFile.getInputStream();
            // 跳过已经上传的分片。
            long skip = inputStream.skip(startPos);
            UploadPartRequest uploadPartRequest = new UploadPartRequest();
            uploadPartRequest.setBucketName(OssUtil.BUCKET_NAME);
            uploadPartRequest.setKey("test/" + fileName);
            uploadPartRequest.setUploadId(uploadId);
            uploadPartRequest.setInputStream(inputStream);
            // 设置分片大小。除了最后一个分片没有大小限制,其他的分片最小为100 KB。
            uploadPartRequest.setPartSize(curPartSize);
            // 设置分片号。每一个上传的分片都有一个分片号,取值范围是1~10000,如果超出此范围,OSS将返回InvalidArgument错误码。
            uploadPartRequest.setPartNumber(i + 1);

            // 每个分片不需要按顺序上传,甚至可以在不同客户端上传,OSS会按照分片号排序组成完整的文件。
            UploadPartResult uploadPartResult = OssUtil.getOssBeanInner().uploadPart(uploadPartRequest);
            // 每次上传分片之后,OSS的返回结果包含PartETag。PartETag将被保存在partETags中。
            partETags.add(uploadPartResult.getPartETag());

            BigDecimal div = BigDecimalUtil.div(i * 100, partCount);
            redisUtils.set("nowN", div.toString());
        }

        // 创建CompleteMultipartUploadRequest对象。
        // 在执行完成分片上传操作时,需要提供所有有效的partETags。OSS收到提交的partETags后,会逐一验证每个分片的有效性。当所有的数据分片验证通过后,OSS将把这些分片组合成一个完整的文件。
        CompleteMultipartUploadRequest completeMultipartUploadRequest =
                new CompleteMultipartUploadRequest(OssUtil.BUCKET_NAME, "test/" + fileName, uploadId, partETags);
        redisUtils.set("nowN", "100", 10000);
        // 完成分片上传。
        CompleteMultipartUploadResult completeMultipartUploadResult = OssUtil.getOssBeanInner().completeMultipartUpload(completeMultipartUploadRequest);
        String previewUrl = OssUtil.getOssBeanOut().generatePresignedUrl("你自己的bucketName","test/" + fileName , new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 50)).toString();
        final String location = completeMultipartUploadResult.getLocation();
        OssUtil.getOssBeanInner().shutdown();
        OssUtil.getOssBeanOut().shutdown();
        Map<String,Object> map = new HashMap<>();
        map.put("previewUrl",URLDecoder.decode(previewUrl,"UTF-8"));
        return RestResult.result(CommonResult.SUCCESS, map);
    }

4,工具类Ossutil 

@Slf4j
public class OssUtil {
    public static final String STYLE = "imm/previewdoc,copy_1";
    /**
     * 查看自己的节点
     * (接口调用的话)内网上传 oss地址
     */
    public static final String INNER_ENDPOINT = "https://oss-cn-beijing-internal.aliyuncs.com";
    /**
     * 查看自己的节点
     * (接口调用的话)外网下载或预览 oss地址  oss-cn-beijing.aliyuncs.com
     */
    public static final String OUT_ENDPOINT = "https://oss-cn-beijing.aliyuncs.com";
    public static final String KEY_ID = "你自己的";
    public static final String KEY_SECRET = "你自己的";
    public static final String BUCKET_NAME = "你自己的";


    public static void delOssFile(String fileName) {
        if (fileName.isEmpty()){
            return;
        }
        OSS oss = new OSSClientBuilder().build(OUT_ENDPOINT, KEY_ID, KEY_SECRET);
        oss.deleteObject(BUCKET_NAME, fileName);
        oss.shutdown();
    }

    public static void delOssFile(String ossPreviewName,String ossDownloadName) {
        if (ossPreviewName.isEmpty()||ossDownloadName.isEmpty()){
            return;
        }
        OSS oss = new OSSClientBuilder().build(OUT_ENDPOINT, KEY_ID, KEY_SECRET);
        oss.deleteObject(BUCKET_NAME, ossPreviewName);
        oss.shutdown();
    }


    public static OSS getOssBeanInner(){
        return new OSSClientBuilder().build(INNER_ENDPOINT, KEY_ID, KEY_SECRET);
    }

    public static OSS getOssBeanOut(){
        return new OSSClientBuilder().build(OUT_ENDPOINT, KEY_ID, KEY_SECRET);
    }

    public static class PutObjectProgressListener implements ProgressListener {
        private HttpSession session;
        private long bytesWritten = 0;
        private long totalBytes = -1;
        private boolean succeed = false;
        private int percent = 0;

        //构造方法中加入session
        public PutObjectProgressListener() {
        }

        public PutObjectProgressListener(HttpSession mSession) {
            this.session = mSession;
            session.setAttribute("upload_percent", percent);
        }

        @Override
        public void progressChanged(ProgressEvent progressEvent) {
            long bytes = progressEvent.getBytes();
            ProgressEventType eventType = progressEvent.getEventType();
            switch (eventType) {
                case TRANSFER_STARTED_EVENT:
                    log.info("Start to upload......");
                    break;
                case REQUEST_CONTENT_LENGTH_EVENT:
                    this.totalBytes = bytes;
                    log.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.0 / this.totalBytes);
                        //将进度percent放入session中
                        session.setAttribute("upload_percent", percent);
                        log.info(bytes + " bytes have been written at this time, upload progress: " + percent + "%(" + this.bytesWritten + "/" + this.totalBytes + ")");
                    } else {
                        log.info(bytes + " bytes have been written at this time, upload ratio: unknown" + "(" + this.bytesWritten + "/...)");
                    }
                    break;
                case TRANSFER_COMPLETED_EVENT:
                    this.succeed = true;
                    log.info("Succeed to upload, " + this.bytesWritten + " bytes have been transferred in total");
                    break;
                case TRANSFER_FAILED_EVENT:
                    log.info("Failed to upload, " + this.bytesWritten + " bytes have been transferred");
                    break;
                default:
                    break;
            }
        }

        public boolean isSucceed() {
            return succeed;
        }
    }

}

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
以下是Java SpringBoot框架下实现OSS文件分片上传进度条展示的示例代码: 1. 首先在pom.xml文件中引入OSS SDK依赖: ```xml <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>2.9.2</version> </dependency> ``` 2. 创建一个名为OSSUtil的工具类,用于连接OSS服务并实现分片上传和进度查询: ```java import com.aliyun.oss.OSS; import com.aliyun.oss.OSSClientBuilder; import com.aliyun.oss.model.*; import org.springframework.stereotype.Component; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; @Component public class OSSUtil { // 阿里云OSS服务的Endpoint private static final String ENDPOINT = "your_endpoint"; // 阿里云OSS服务的AccessKeyId private static final String ACCESS_KEY_ID = "your_access_key_id"; // 阿里云OSS服务的AccessKeySecret private static final String ACCESS_KEY_SECRET = "your_access_key_secret"; // 阿里云OSS服务的BucketName private static final String BUCKET_NAME = "your_bucket_name"; // OSS客户端实例 private OSS ossClient; // 初始化OSS客户端 public void init() { ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET); } // 关闭OSS客户端 public void close() { ossClient.shutdown(); } // 分片上传文件 public void multipartUpload(String objectName, InputStream inputStream, long fileSize) throws IOException { // 初始化MultipartUploadRequest InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(BUCKET_NAME, objectName); InitiateMultipartUploadResult result = ossClient.initiateMultipartUpload(request); // 获取UploadId String uploadId = result.getUploadId(); // 设置每个Part的大小,最小为100KB final long partSize = 1024 * 100L; // 计算文件的Part个数 int partCount = (int) (fileSize / partSize); if (fileSize % partSize != 0) { partCount++; } List<PartETag> partETags = new ArrayList<>(); for (int i = 0; i < partCount; i++) { long startPos = i * partSize; long curPartSize = Math.min(partSize, fileSize - startPos); InputStream partInputStream = new FilePartInputStream(inputStream, startPos, curPartSize); UploadPartRequest uploadPartRequest = new UploadPartRequest(); uploadPartRequest.setBucketName(BUCKET_NAME); uploadPartRequest.setKey(objectName); uploadPartRequest.setUploadId(uploadId); uploadPartRequest.setPartNumber(i + 1); uploadPartRequest.setInputStream(partInputStream); uploadPartRequest.setPartSize(curPartSize); UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest); partETags.add(uploadPartResult.getPartETag()); } // 完成分片上传 CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(BUCKET_NAME, objectName, uploadId, partETags); ossClient.completeMultipartUpload(completeMultipartUploadRequest); } // 获取文件上传进度 public long getUploadProgress(String objectName) { // 获取文件大小 ObjectMetadata metadata = ossClient.getObjectMetadata(BUCKET_NAME, objectName); long fileSize = metadata.getContentLength(); // 获取已上传的Part信息 ListPartsRequest listPartsRequest = new ListPartsRequest(BUCKET_NAME, objectName, null); PartListing partListing = ossClient.listParts(listPartsRequest); List<PartSummary> partSummaries = partListing.getParts(); // 计算已上传的大小 long uploadedSize = 0; for (PartSummary partSummary : partSummaries) { uploadedSize += partSummary.getSize(); } // 计算上传进度 long progress = (long) ((double) uploadedSize / fileSize * 100); return progress; } // 自定义的输入流,用于分片上传文件 private static class FilePartInputStream extends InputStream { private InputStream inputStream; private long startPos; private long curPartSize; private long pos; public FilePartInputStream(InputStream inputStream, long startPos, long curPartSize) { this.inputStream = inputStream; this.startPos = startPos; this.curPartSize = curPartSize; this.pos = 0; } @Override public int read() throws IOException { if (pos >= curPartSize) { return -1; } int read = inputStream.read(); pos++; return read; } @Override public int read(byte[] b, int off, int len) throws IOException { if (pos >= curPartSize) { return -1; } int readLen = (int) Math.min(len, curPartSize - pos); int read = inputStream.read(b, off, readLen); pos += read; return read; } @Override public void close() throws IOException { super.close(); inputStream.close(); } } } ``` 3. 创建一个名为UploadController的控制器,用于处理文件上传和进度查询请求: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; @RestController @RequestMapping("/upload") public class UploadController { @Autowired private OSSUtil ossUtil; // 处理文件上传请求 @PostMapping("/file") public String uploadFile(@RequestParam("file") MultipartFile file) throws IOException { // 初始化OSS客户端 ossUtil.init(); // 分片上传文件 String objectName = file.getOriginalFilename(); ossUtil.multipartUpload(objectName, file.getInputStream(), file.getSize()); // 关闭OSS客户端 ossUtil.close(); return "success"; } // 处理进度查询请求 @GetMapping("/progress") public long getUploadProgress(@RequestParam("objectName") String objectName) { // 获取上传进度 long progress = ossUtil.getUploadProgress(objectName); return progress; } } ``` 4. 在前端页面中使用HTML5的进度条组件,结合后端实现的上传进度查询接口,展示文件上传的进度。例如,以下是一个基于Vue.js的示例代码: ```html <template> <div> <input type="file" @change="handleFileChange"> <button @click="uploadFile">上传</button> <div v-show="showProgress"> <progress :value="progress" max="100"></progress> <span>{{ progress }}%</span> </div> </div> </template> <script> export default { data() { return { file: null, showProgress: false, progress: 0, intervalId: null } }, methods: { handleFileChange(event) { this.file = event.target.files[0] }, uploadFile() { this.showProgress = true let formData = new FormData() formData.append('file', this.file) axios.post('/upload/file', formData) .then(response => { console.log(response) }) .catch(error => { console.log(error) }) this.intervalId = setInterval(() => { axios.get('/upload/progress', { params: { objectName: this.file.name }}) .then(response => { this.progress = response.data if (this.progress >= 100) { clearInterval(this.intervalId) } }) .catch(error => { console.log(error) }) }, 1000) } } } </script> ``` 以上就是Java SpringBoot框架下实现OSS文件分片上传进度条展示的全代码示例。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值