目的:将文件交给阿里云进行管理,可避免文件对本地服务器资源的占用,阿里云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的类)

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<String> objectNames) {
try {
// 批量删除文件。
DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(objectNames));
List<String> 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);
}
}
} }

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

文件上传接入阿里云OSS的更多相关文章

  1. Windows环境下用C#编程将文件上传至阿里云OSS笔记

    Windows环境下用C#编程将文件上传至阿里云OSS笔记 本系列文章由ex_net(张建波)编写,转载请注明出处. http://blog.csdn.net/ex_net/article/detai ...

  2. Java下载https文件上传到阿里云oss服务器

    Java下载https文件上传到阿里云oss服务器 今天做了一个从Https链接中下载音频并且上传到OSS服务器,记录一下希望大家也少走弯路. 一共两个类: 1 .实现自己的证书信任管理器类 /** ...

  3. ThinkPHP 文件上传到阿里云OSS上(干货)

    参考:http://www.thinkphp.cn/extend/789.html 1.前往阿里云github下载SDK包:https://github.com/aliyun/aliyun-oss-p ...

  4. django 文件上传(阿里云oss)下载(支持大文件下载)

    1.文件上传 Models 设计 class Upload_File(models.Model): image = models.FileField(upload_to='file/%Y/%m',de ...

  5. springmvc学习笔记--支持文件上传和阿里云OSS API简介

    前言: Web开发中图片上传的功能很常见, 本篇博客来讲述下springmvc如何实现图片上传的功能. 主要讲述依赖包引入, 配置项, 本地存储和云存储方案(阿里云的OSS服务). 铺垫: 文件上传是 ...

  6. 备份MySQL数据库并上传到阿里云OSS存储

    1. 环境配置 要将本地文件上传到阿里云oss中, 必须使用阿里云提供的工具 ossutil, 有32位,也有64位的, Linux和Windows都有.具体可以到阿里云官网下载 官网及文档: htt ...

  7. Linux本地数据上传到阿里云OSS

    这篇文章主要是介绍如何将服务器本地的数据上传到阿里云OSS的指定bucket中,最重要的参考文档是数据迁移单机部署.我第一次上传数据到OSS上时,步骤要比前面的链接中介绍的要麻烦,ossimport工 ...

  8. 前端(react)上传到阿里云OSS存储 实例

    需求背景 由于现有的后台管理系统,上传的视频越来越大,加上上传视频较慢,后端小哥提出直接从前端上传视频或者其他文件到阿里云OSS存储. 阿里云OSS 阿里云OSS文档介绍,这里不做过多赘述 安装 原本 ...

  9. JavaScript进阶(九)JS实现本地文件上传至阿里云服务器

    JS实现本地文件上传至阿里云服务器 前言 在前面的博客< JavaScript进阶(八)JS实现图片预览并导入服务器功能>(点击查看详情)中,实现了JS将本地图片文件预览并上传至阿里云服务 ...

随机推荐

  1. linux篇--mysql数据库备份并删除前一分钟的数据

    linux 中mysql数据库定时备份并删除前一分钟的所有数据 #!/bin/bash #mysqldump -uroot -ppassword01! imaginebase > /home/b ...

  2. 使用 gitbook 制作自己的 html 文档

    使用 gitbook 制作自己的 html 文档 步骤如下 npm install gitbook-cli -g // 全局安装 gitbook-cli <span style="te ...

  3. github新项目npm错误

    当我们从GitHub或者别人那里拿到项目的时候,一般都是要先npm install 进行安装依赖.但是难免会遇到报错. 出现问题1: 解决方案:清除缓存npm cache clear --force之 ...

  4. django框架10

    内容概要 ajax结合sweetalert forms组件钩子函数 forms组件字段参数 forms组件字段类型 forms组件源码分析 cookie与session简介 django操作cooki ...

  5. 2020.12.12【NOIP提高B组】模拟 总结

    第一次来 B 组做,虚的很 T1: 容斥原理 比赛时也打了个大致,但挂了,只有 50 分. 赛后重构了一下代码,AC \(UPDATE:2020/12/13\ \ \ 14:10\) 思路: 像前缀和 ...

  6. docker引起服务器磁盘爆满

    服务器异常 又是开开心心打开我心爱的服务器一天: 吔!这是嘛啊?我的服务器域名访问不了了,一直转圈圈超时了,好,打开ssh远程看看,吔!!!还是访问不了,宕机了?怀着一颗憋大便的心情打开了阿里云控制面 ...

  7. 【Golang】创建有配置参数的结构体时,可选参数应该怎么传?

    写在前面的话 Golang中构建结构体的时候,需要通过可选参数方式创建,我们怎么样设计一个灵活的API来初始化结构体呢. 让我们通过如下的代码片段,一步一步说明基于可选参数模式的灵活 API 怎么设计 ...

  8. C++对象间通信组件,让C++对象“无障碍交流”

    介绍 这是很久之前的一个项目了,最近刚好有些时间,就来总结一下吧! 推荐初步熟悉项目后阅读本文: https://gitee.com/smalldyy/easy-msg-cpp 从何而来 这要从我从事 ...

  9. 在公网服务器搭建CobaltStrike4.0

    因为工作需要使用cs,正好之前腾讯云薅了一把羊毛,就把VPS装起来cs. 选的环境是centos7.6 cs运行需要java环境 先使用yum -y list java* 查看yum存在的java库 ...

  10. WPF第三方控件,只能输入数字型数据

    话不多说,根据最近项目需求,为了减少输入验证等相关代码量,需要此控件 先上效果图 默认样式是这样,自己可以根据需求修改外形,但我更喜欢它自带的简洁版 有人可能会问怎么实现的呢?其实很简单,我们设置它的 ...