• 文件上传接入阿里云OSS


    目的:将文件交给阿里云进行管理,可避免文件对本地服务器资源的占用,阿里云OSS还可根据读写偏好选择合适的文件存储类型服务器,文件异地备份等

    一、阿里云OSS基础了解(前提)

    1、存储空间(Bucket)

    用于存储对象(Object)的容器,同一个存储空间的内部是扁平的,没有文件系统的目录等概念,所有的对象都必须隶属于某个存储空间。存储空间具有各种配置属性,包括地域、访问权限、存储类型等。可根据实际需求,创建不同存储空间存储不同数据。(百度的官话)

    简而言之:Bucket就简单理解为C盘,D盘就可以了,可以在不同的Bucket下创建文件夹存储文件。

    2、存储对象(Object)

    是 OSS 存储数据的基本单元,也被称为 OSS 的文件。对象由元信息(Object Meta)、用户数据(Data)和文件名(Key)组成。对象由存储空间内部唯一的 Key 来标识。对象元信息是一组键值对,表示了对象的一些属性,比如最后修改时间、大小等信息,支持在元信息中存储一些自定义的信息。对象的生命周期是从上传成功到被删除为止。(还是官话)

    简而言之:就是要存储的文件。

    二、环境准备及测试

    1、pom坐标导入

    复制代码
    
    <dependency>
        <groupId>com.aliyun.ossgroupId>
        <artifactId>aliyun-sdk-ossartifactId>
        <version>3.10.2version>
    dependency>
    复制代码

    2、阿里云OSS API操作返回值类

    复制代码
    package cc.mrbird.febs.finance.domain.dto;
    
    import lombok.Data;
    
    /**
     * @Author: sunguoqiang
     * @Description: 阿里云上传结果集
     * @DateTime: 2022/8/3 16:27
     **/
    @Data
    public class AliyunOssResult {
        /**
         * code:200成功
         * code: 400失败
         */
        private int code;
        /**
         * 上传成功的返回url
         */
        private String url;
        /**
         * 提示信息
         */
        private String msg;
    }
    复制代码

    3、阿里云OSS API操作工具类(直接调用阿里云OSS API的类)

    package cc.mrbird.febs.finance.util;
    
    import cc.mrbird.febs.finance.domain.dto.AliyunOssResult;
    import cc.mrbird.febs.finance.exception.FileUploadException;
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import com.aliyun.oss.model.*;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.PostConstruct;
    import java.io.*;
    import java.net.URL;
    import java.util.Date;
    import java.util.List;
    
    /**
     * @Author: sunguoqiang
     * @Description: TODO
     * @DateTime: 2022/8/3 16:23
     **/
    @Component
    @Slf4j
    public class AliyunOSSUtil {
    
        @Value("${aliyunOss.endpoint}")
        private String endpoint;
        @Value("${aliyunOss.accessKeyId}")
        private String accessKeyId;
        @Value("${aliyunOss.accessKeySecret}")
        private String accessKeySecret;
        @Value("${aliyunOss.bucketName}")
        private String bucketName;
        @Value("${aliyunOss.urlPrefix}")
        private String urlPrefix;
    
        private OSS ossClient;
    
        /**
         * 初始化OssClient
         */
        @PostConstruct
        public void generateOSS() {
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
            if (ossClient != null) {
                this.ossClient = ossClient;
            } else {
                log.error("OSS对象实例化失败.");
                throw new RuntimeException("OSS对象实例化失败.");
            }
        }
    
        /**
         * 判断阿里云bucket下是否存在filePath文件夹,不存在则创建
         *
         * @param filePath
         */
        public void isExistAndCreateFolder(String filePath) {
            if (!isExists(filePath)) {
                boolean mkdirs = createFolder(filePath);
                if (!mkdirs) {
                    throw new FileUploadException("附件文件夹创建失败!");
                }
            }
        }
    
        /**
         * 上传文件,以IO流方式
         *
         * @param inputStream 输入流
         * @param objectName  唯一objectName(在oss中的文件名字)
         */
        public AliyunOssResult upload(InputStream inputStream, String objectName) {
            AliyunOssResult aliyunOssResult = new AliyunOssResult();
            try {
                // 上传内容到指定的存储空间(bucketName)并保存为指定的文件名称(objectName)。
                PutObjectResult putObject = ossClient.putObject(bucketName, objectName, inputStream);
                // 关闭OSSClient。
                ossClient.shutdown();
                aliyunOssResult.setCode(200);
                aliyunOssResult.setUrl(urlPrefix + objectName);
                aliyunOssResult.setMsg("上传成功");
            } catch (Exception e) {
                e.printStackTrace();
                aliyunOssResult.setCode(400);
                aliyunOssResult.setMsg("上传失败");
            }
            return aliyunOssResult;
        }
    
        /**
         * 获取oss文件
         *
         * @param folderName
         * @return
         */
        public OSSObject get(String folderName) {
            OSSObject ossObject = ossClient.getObject(bucketName, folderName);
            return ossObject;
        }
    
        /**
         * 删除OSS中的单个文件
         *
         * @param objectName 唯一objectName(在oss中的文件名字)
         */
        public void delete(String objectName) {
            try {
                ossClient.deleteObject(bucketName, objectName);
                // 关闭OSSClient。
                ossClient.shutdown();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 批量删除OSS中的文件
         *
         * @param objectNames oss中文件名list
         */
        public void delete(List objectNames) {
            try {
                // 批量删除文件。
                DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(objectNames));
                List deletedObjects = deleteObjectsResult.getDeletedObjects();
                // 关闭OSSClient。
                ossClient.shutdown();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 获取文件临时url
         *
         * @param objectName    oss中的文件名
         * @param effectiveTime 有效时间(ms)
         */
        public String getUrl(String objectName, long effectiveTime) {
            // 设置URL过期时间
            Date expiration = new Date(new Date().getTime() + effectiveTime);
            GeneratePresignedUrlRequest generatePresignedUrlRequest;
            generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName, objectName);
            generatePresignedUrlRequest.setExpiration(expiration);
            URL url = ossClient.generatePresignedUrl(generatePresignedUrlRequest);
            return url.toString();
        }
    
        /**
         * oss拷贝文件
         *
         * @param sourcePath
         * @param targetPath
         */
        public void copyFileSourceToTarget(String sourcePath, String targetPath) throws FileNotFoundException {
            try {
                CopyObjectResult copyObjectResult = ossClient.copyObject(bucketName, sourcePath, bucketName, targetPath);
            } catch (Exception e) {
                throw new FileUploadException("文件转移操作异常.");
            }
        }
    
        /**
         * 根据文件路径获取输出流
         *
         * @param filePath
         * @return
         * @throws IOException
         */
        public InputStream getInputStream(String filePath) throws IOException {
            if (filePath == null || filePath.isEmpty()) {
                log.error("方法[getInputStream]参数[filePath]不能为空.");
                return null;
            }
            OSSObject object = ossClient.getObject(bucketName, filePath);
            InputStream input = object.getObjectContent();
            byte[] bytes = toByteArray(input);
            return new ByteArrayInputStream(bytes);
        }
    
        /**
         * InputStream流转byte数组
         *
         * @param input
         * @return
         * @throws IOException
         */
        private static byte[] toByteArray(InputStream input) throws IOException {
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            byte[] buffer = new byte[input.available()];
            int n = 0;
            while (-1 != (n = input.read(buffer))) {
                output.write(buffer, 0, n);
            }
            return output.toByteArray();
        }
    
        /**
         * 创建文件夹
         *
         * @param folderName
         * @return
         */
        private Boolean createFolder(String folderName) {
            if (folderName == null || folderName.isEmpty()) {
                log.error("方法[createFolder]参数[folderName]不能为空.");
                return false;
            }
            // 防止folderName文件夹因为末尾未加【/】导致将目录当做文件创建
            if (!folderName.substring(folderName.length() - 1).equals("/")) {
                folderName = folderName + "/";
            }
            // 创建文件夹
            try {
                ossClient.putObject(bucketName, folderName, new ByteArrayInputStream(new byte[0]));
                log.info("附件文件夹[" + folderName + "]创建成功.");
                return true;
            } catch (Exception e) {
                log.error("附件文件夹[" + folderName + "]创建失败.");
                return false;
            }
        }
    
        /**
         * 判断文件夹是否存在
         *
         * @param folderName
         * @return
         */
        private Boolean isExists(String folderName) {
            return ossClient.doesObjectExist(bucketName, folderName);
        }
    
        /**
         * 根据文件路径获取File
         *
         * @param filePath
         * @return
         * @throws IOException
         */
        public File getFile(String filePath) throws IOException {
            if (filePath == null || filePath.isEmpty()) {
                log.error("方法[getFile]参数[filePath]不能为空.");
                return null;
            }
            File file = new File(filePath);
            InputStream inputStream = getInputStream(filePath);
            copyInputStreamToFile(inputStream, file);
            return file;
        }
    
        /**
         * InputStream -> File
         *
         * @param inputStream
         * @param file
         * @throws IOException
         */
        private static void copyInputStreamToFile(InputStream inputStream, File file) throws IOException {
            try (FileOutputStream outputStream = new FileOutputStream(file)) {
                int read;
                byte[] bytes = new byte[1024];
                while ((read = inputStream.read(bytes)) != -1) {
                    outputStream.write(bytes, 0, read);
                }
            }
        }
    
    
    }
    View Code

    4、如何使用?

    1、控制器

    @PostMapping("/avatar")
    public Result updateAvatar(@RequestParam("file") MultipartFile multipartFile, String username) throws Exception {
        return Result.success(userService.updateAvatar(multipartFile, username));
    }

    2、service层

    复制代码
    @Override
    public String updateAvatar(MultipartFile multipartFile, String username) throws Exception {
        String avatarUrl = fileService.uploadAvatar(multipartFile);
        User user = new User();
        user.setAvatar(avatarUrl);
        LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, username);
        this.baseMapper.update(user, queryWrapper);
        return avatarUrl;
    }
    复制代码

    第一句调用FileService的上传头像方法。

    3、FileService上传头像方法

    复制代码
    @Override
    public String uploadAvatar(MultipartFile multipartFile) {
        // 设置文件上传位置
        String currentDate = DateUtil.formatNowLocalDate("yyyyMMdd");
        String actualFileName = FileUtils.forStringFilter(System.nanoTime() + multipartFile.getOriginalFilename());
        // 预设上传文件到阿里云oss的返回值
        AliyunOssResult uploadResult = null;
        try {
            InputStream inputStream = new ByteArrayInputStream(multipartFile.getBytes());
            String filePath = basePath + separator + SYSTEM + separator + currentDate;
            // 判断是否存在文件夹,不存在则创建
            aliyunOSSUtil.isExistAndCreateFolder(filePath);
            // 上传文件
            uploadResult = aliyunOSSUtil.upload(inputStream, filePath + separator + actualFileName);
        }
        catch (IOException e) {
            log.error("附件上传出现错误:{}", e.getMessage(), e);
            throw new SystemException("服务器异常");
        }
        return uploadResult.getUrl();
    }
    复制代码

    FileService中的保存文件方法都会将各种上传的文件通过AliyunOSSUtil工具类上传至阿里云OSS。

    三、报销接入遇到的问题

    1、如果FileService内部方法获取的是MultipartFile类型文件需要上传。

    复制代码
    @Override
    @Transactional(rollbackFor = Exception.class)
    public FileInfoDTO uploadAttachFile(List fileList, Integer source, Integer fileType, String spNo) {
        try {
            for (int i = 0; i < fileList.size(); i++) {
                MultipartFile part = fileList.get(i);
                String originalFileName = part.getOriginalFilename();
                String actualFileName = FileUtils.forStringFilter(System.nanoTime() + part.getOriginalFilename());
                // 设置文件上传位置
                String currentDate = DateUtil.formatNowLocalDate("yyyyMMdd");
                String filePath = basePath + separator + UPLOAD + separator + currentDate;
                // 判断是否存在文件夹,不存在则创建
                aliyunOSSUtil.isExistAndCreateFolder(filePath);
                InputStream inputStream = new ByteArrayInputStream(part.getBytes());
                // 上传文件
                AliyunOssResult ossResult = aliyunOSSUtil.upload(inputStream, filePath + separator + actualFileName);
                String mediaId = null;
                // 附件上传服务器之后还会再上传企业微信服务器(暂时忽略这一步)
                if (FileSourceEnum.isNeedUploadWeChat(source) && i < UPLOAD_FILE_NUM_LIMIT - attachFileList.size() && FileTypeEnum.isApply(fileType)) {
                    mediaId = weChatManager.uploadFileWithInputStream(part);
                    mediaIdList.add(mediaId);
                }
                // 存储附件表
                this.generateAndAddAttachFile(originalFileName, currentDate + separator + actualFileName, source, fileType, part.getSize(), mediaId, spNo, attachFileIdList, ossResult.getUrl());
            }
            return fileInfoDTO;
        }
        catch (IOException e) {
            log.error("附件上传出现错误:{}", e.getMessage(), e);
            throw new SystemException("服务器异常");
        }
    }
    复制代码

    由于AliyunOSSUtil中上传方法使用的是InputStream上传的方式,因此可将MultipartFile类型转换成InputStream进行上传。

    2、如果需要上传的文件是iText程序中生成的。

    如果文件不是前端传递,而是程序中运行时生成的,而且不能将运行时生成的文件保存在服务器中。例如iText运行时生成文件需要上传至阿里云oss。

    复制代码
    public String generatePreApplyPdf(String spNo) throws DocumentException, FileNotFoundException {
        Map userMap = userService.toMap(null);
        Map<long, String> deptMap = deptService.toMap(null);
        PreApplyInfo preApplyInfo = preApplyInfoService.findBySpNo(spNo);
        Document document = new Document();
        document.setPageSize(PageSize.A4.rotate());
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        PdfWriter pdfWriter = generatePdfWriter(document, bos);
        document.open();
        fillTitle(document, preApplyInfo);
        if (FeeTypeEnum.isTravel(preApplyInfo.getFeeType())) {
            fillTravelHeader(document, userMap.get(preApplyInfo.getApplicant()), cn.hutool.core.date.DateUtil.format(preApplyInfo.getApplyTime(), DatePatternEnum.CS_D.getPattern()));
            fillTravelLines(document, preTravelDetailService.listPreTravelDetailBySpNo(spNo), preApplyInfo);
            document.add(generateBlankParagraph());
            fillTravelApprovalInfo(document, auditService.listPreApplyApprovalNodeVO(spNo), userMap);
            fillTravelRemark(document);
        } else {
            fillOtherHead(document, preApplyInfo, userMap, deptMap);
            fillOtherLines(document, preApplyInfo, preApplyLineService.listPreApplyDetailBySpNo(spNo));
            fillOtherApprovalInfo(document, auditService.listPreApplyApprovalNodeVO(spNo), userMap);
        }
        document.close();
        pdfWriter.close();
        // 阿里云oss上传pdf
        ByteArrayInputStream swapStream = new ByteArrayInputStream(bos.toByteArray());
        String pdfFileFolder = basePath + separator + PDF + separator;
        String pdfFileName = basePath + separator + PDF + separator + spNo + PDF_SUFFIX;
        aliyunOSSUtil.isExistAndCreateFolder(pdfFileFolder);
        AliyunOssResult aliyunOssResult = aliyunOSSUtil.upload(swapStream, pdfFileName);
        return aliyunOssResult.getCode() == 200 ? aliyunOssResult.getUrl() : "PDF文件生成失败.";
    }
    复制代码

    第八行:generatePdfWriter方法

    复制代码
    public PdfWriter generatePdfWriter(Document document, OutputStream out) throws FileNotFoundException, DocumentException {
        PdfWriter writer = PdfWriter.getInstance(document, out);
        writer.setPageEvent(new FootHelper());
        return writer;
    }
    复制代码

    1、该部分是将 document(程序运行时生成的pdf内容) 写入 out(输出流) 中。

    2、当执行到22、23行时:

             document.close()
             pdfWriter.close()

    3、表明PDF生成完毕,已经将document中的内容写入out输出流中。

    4、25行 ByteArrayInputStream swapStream = new ByteArrayInputStream(bos.toByteArray())  将输出流转成输入流,有了输入流就可以调用AliyunOSSUtil工具类进行上传文件。

    3、在上述附件需要上传两个位置(阿里云服务器、企业微信服务器)

    阿里云上传可以调用工具类进行操作,上传企业微信服务器用到RestTemplate进行操作。由于文件不在本地存储,因此无法得到File类型文件,文件类型可能是MultipartFile类型、InputStream类型,因此RestTemplate有如下几种上传文件方式供参考。

    参见 https://www.cnblogs.com/sun-10387834/p/16554574.html

     

  • 相关阅读:
    零基础学韩语-看韩剧追欧巴
    Apache Commons Text 库简介
    Dubbo笔记
    车载软件架构——基础软件供应商&开发工具链(一)
    【建议背诵】软考高项考试案例简答题汇总~(1)
    C语言数组相关问题深度理解
    JavaScript(WebAPI) (前端)
    Socks5代理IP在跨境电商与网络游戏中的网络安全应用
    Xilinx 7系列FPGA的配置流程
    Python 核心编程
  • 原文地址:https://www.cnblogs.com/sun-10387834/p/16563242.html