7

文件上传接入阿里云OSS - 佛祖让我来巡山

 2 years ago
source link: https://www.cnblogs.com/sun-10387834/p/16563242.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
目的:将文件交给阿里云进行管理,可避免文件对本地服务器资源的占用,阿里云OSS还可根据读写偏好选择合适的文件存储类型服务器,文件异地备份等

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

1、存储空间(Bucket)

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

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

2、存储对象(Object)

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

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

二、环境准备及测试

1、pom坐标导入

<!-- 阿里云OSS -->
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.10.2</version>
</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的类)

ContractedBlock.gifExpandedBlockStart.gif

View Code

4、如何使用?

1、控制器

@PostMapping("/avatar")
public Result<String> 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<User> 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<MultipartFile> 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<String, String> 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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK