
我们可以理解为就是一个资源服务器,在这之前我也尝试过Nginx当静态资源服务器,但效果比较一般,为什么选择阿里云OSS,只是因为最近刚好公司用到了,所以就接入了,还有其他的比如七牛云,腾讯云等等。
创建 BucketBucket 就是存储空间,我们的文件放在那里。具体操作如下:
1、点击进入阿里云OSS,在 Bucket 列表创建自己的存储空间。
2、填写空间名,选择地域节点等操作。
我们的存储空间创建好了,下一步就是集成到我们的项目当中。
项目集成在阿里云的官方文档中提供了不同语言SDK示例,可自行参考。
场景在操作之前,先讲一讲我使用的场景。
在我负责的模块需要提供一个上传文件的服务,从消息队列中去读取数据,然后把数据转为 excel 文件在上传到 oss 中去,并返回一个文件的路径。当然,不只是上传 excel ,任何文件都可以。
maven 依赖首先是 oss 的依赖,我用的是官方文档中提供的版本:
com.aliyun.oss aliyun-sdk-oss 3.10.2
因为涉及到 excel 的生成,我直接使用开源的 easypoi,对于 poi 操作有一大堆开源的,如果不是需要定制开发,我一般都是用开源的,还有阿里的 easyexcel 也是不错的。
cn.afterturn easypoi-base 4.1.0 cn.afterturn easypoi-web 4.1.0 cn.afterturn easypoi-annotation 4.1.0
还用到了一个工具类Hutool,Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率。你想要的他都有。
工具类cn.hutool hutool-all 5.7.13
1、根据官方提供的文档封装的工具类。
再此之前你需要获得这几项配置:
import cn.hutool.core.date.DateUtil;
import com.fanryes.vanadium.cloud.exception.OssFileException;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
public class AliyunOssApiClient {
private String endpoint = "你的endpoint";
private String accessKeyId = "你的accessKeyId";
private String accessKeySecret = "你的accessKeySecret";
private String url = "你的Bucket 域名";
private String bucketName = "你的Bucket名称";
private OssApi ossApi;
public AliyunOssApiClient init() {
ossApi = new OssApi(this.endpoint, this.accessKeyId, this.accessKeySecret);
this.url = url;
this.bucketName = bucketName;
return this;
}
public String uploadFileExpired(InputStream is, String pathPrefix, String suffix) {
String prefix = pathPrefix.endsWith("/") ? pathPrefix : pathPrefix + "/";
String fileName = prefix + DateUtil.format(new Date(), "yyyyMMddHHmmssSSS") + suffix;
this.check();
try {
return ossApi.uploadFileExpired(is, fileName, bucketName);
} catch (Exception e) {
throw new OssFileException("文件上传失败:" + e.getMessage());
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public String uploadFile(InputStream is, String pathPrefix, String suffix) {
String prefix = pathPrefix.endsWith("/") ? pathPrefix : pathPrefix + "/";
String fileName = prefix + DateUtil.format(new Date(), "yyyyMMddHHmmssSSS") + suffix;
this.check();
try {
return ossApi.uploadFile(is, fileName, bucketName, endpoint);
} catch (Exception e) {
throw new OssFileException("文件上传失败:" + e.getMessage());
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void downloadLocalFile(String fileName, String path) {
this.check();
try {
ossApi.downloadLocalFile(fileName, path, bucketName);
} catch (Exception e) {
throw new OssFileException("文件下载失败:" + e.getMessage());
}
}
public boolean removeFile(String path) {
this.check();
if (StringUtils.isEmpty(path)) {
throw new OssFileException("删除文件失败:文件key为空");
}
try {
this.ossApi.deleteFile(path, bucketName);
return true;
} catch (Exception e) {
throw new OssFileException(e.getMessage());
}
}
public void check() {
if (null == ossApi) {
throw new OssFileException("尚未配置阿里云OSS,文件上传功能暂时不可用!");
}
}
}
2、由于我这里还有其他操作,所以把它的操作和实际业务操作分开了,单独提供了一个Api。
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.CreateBucketRequest;
import com.aliyun.oss.model.GetObjectRequest;
import com.aliyun.oss.model.ListObjectsRequest;
import com.aliyun.oss.model.OSSObjectSummary;
import com.aliyun.oss.model.ObjectListing;
import com.aliyun.oss.model.ObjectPermission;
import com.aliyun.oss.model.StorageClass;
import com.fanryes.vanadium.cloud.domain.ObjectsRequest;
import com.fanryes.vanadium.cloud.exception.OssFileException;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
public class OssApi {
private OSSClient client;
public OssApi(OSSClient client) {
this.client = client;
}
public OssApi(String endpoint, String accessKeyId, String accessKeySecret) {
this.client = new OSSClient(endpoint, accessKeyId, accessKeySecret);
}
public String authFile(String fileName, String bucketName, long expirationTime) {
try {
if (!this.client.doesBucketExist(bucketName)) {
throw new OssFileException("[阿里云OSS] 无法授权访问文件的URL!Bucket不存在:" + bucketName);
}
if (!this.client.doesObjectExist(bucketName, fileName)) {
throw new OssFileException("[阿里云OSS] 文件授权失败!文件不存在:" + bucketName + "/" + fileName);
}
// 设置URL过期时间为1小时
Date expiration = new Date(new Date().getTime() + expirationTime * 1000);
// 生成URL
return this.client.generatePresignedUrl(bucketName, fileName, expiration).toString();
} finally {
this.shutdown();
}
}
public boolean isExistFile(String fileName, String bucketName) {
try {
if (!this.client.doesBucketExist(bucketName)) {
throw new OssFileException("[阿里云OSS] Bucket不存在:" + bucketName);
}
return this.client.doesObjectExist(bucketName, fileName);
} finally {
this.shutdown();
}
}
public ObjectPermission getFileAcl(String fileName, String bucketName) {
try {
if (!this.client.doesBucketExist(bucketName)) {
throw new OssFileException("[阿里云OSS] 无法获取文件的访问权限!Bucket不存在:" + bucketName);
}
if (!this.client.doesObjectExist(bucketName, fileName)) {
throw new OssFileException("[阿里云OSS] 无法获取文件的访问权限!文件不存在:" + bucketName + "/" + fileName);
}
return this.client.getObjectAcl(bucketName, fileName).getPermission();
} finally {
this.shutdown();
}
}
public List listFile(String bucketName, ObjectsRequest request) {
try {
if (!this.client.doesBucketExist(bucketName)) {
throw new OssFileException("[阿里云OSS] 无法获取文件列表!Bucket不存在:" + bucketName);
}
ListObjectsRequest listRequest = new ListObjectsRequest(bucketName);
if (null != request) {
listRequest.withDelimiter(request.getDelimiter())
.withEncodingType(request.getEncodingType())
.withMarker(request.getMarker())
.withMaxKeys(request.getMaxKeys())
.withPrefix(request.getPrefix());
}
// 列举Object
ObjectListing objectListing = this.client.listObjects(listRequest);
return objectListing.getObjectSummaries();
} finally {
this.shutdown();
}
}
public void setFileAcl(String fileName, String bucketName, CannedAccessControlList acl) {
try {
boolean exists = this.client.doesBucketExist(bucketName);
if (!exists) {
throw new OssFileException("[阿里云OSS] 无法修改文件的访问权限!Bucket不存在:" + bucketName);
}
if (!this.client.doesObjectExist(bucketName, fileName)) {
throw new OssFileException("[阿里云OSS] 无法修改文件的访问权限!文件不存在:" + bucketName + "/" + fileName);
}
this.client.setObjectAcl(bucketName, fileName, acl);
} finally {
this.shutdown();
}
}
public void deleteFile(String fileName, String bucketName) {
try {
boolean exists = this.client.doesBucketExist(bucketName);
if (!exists) {
throw new OssFileException("[阿里云OSS] 文件删除失败!Bucket不存在:" + bucketName);
}
if (!this.client.doesObjectExist(bucketName, fileName)) {
throw new OssFileException("[阿里云OSS] 文件删除失败!文件不存在:" + bucketName + "/" + fileName);
}
this.client.deleteObject(bucketName, fileName);
} finally {
this.shutdown();
}
}
public void createBucket(String bucketName) {
try {
boolean exists = this.client.doesBucketExist(bucketName);
if (exists) {
throw new OssFileException("[阿里云OSS] Bucket创建失败!Bucket名称[" + bucketName + "]已被使用!");
}
// -- 创建指定类型的Bucket,请使用Java SDK 2.6.0及以上版本。
CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
// 设置bucket权限为公共读,默认是私有读写
createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
// 设置bucket存储类型为低频访问类型,默认是标准类型
createBucketRequest.setStorageClass(StorageClass.IA);
this.client.createBucket(createBucketRequest);
} finally {
this.shutdown();
}
}
public void createFolder(String folder, String bucketName) throws OssFileException {
try {
if (null == bucketName) {
throw new OssFileException("[阿里云OSS] 尚未指定Bucket!");
}
if (!this.client.doesBucketExist(bucketName)) {
throw new OssFileException("[阿里云OSS] 无法创建目录!Bucket不存在:" + bucketName);
}
folder = folder.endsWith("/") ? folder : folder + "/";
this.client.putObject(bucketName, folder, new ByteArrayInputStream(new byte[0]));
} finally {
this.shutdown();
}
}
public String uploadFileExpired(InputStream inputStream, String fileName, String bucketName) {
try {
if (!this.client.doesBucketExist(bucketName)) {
throw new OssFileException("[阿里云OSS] 无法上传文件!Bucket不存在:" + bucketName);
}
//上传文件
this.client.putObject(bucketName, fileName, inputStream);
//给文件授权,包含过期时间
return this.authFile(fileName, bucketName, 1000);
} finally {
this.shutdown();
}
}
public String uploadFile(InputStream inputStream, String fileName, String bucketName, String endpoint) {
try {
if (!this.client.doesBucketExist(bucketName)) {
throw new OssFileException("[阿里云OSS] 无法上传文件!Bucket不存在:" + bucketName);
}
//上传文件
this.client.putObject(bucketName, fileName, inputStream);
return "https://" + bucketName + "." + endpoint + "/" + fileName;
} finally {
this.shutdown();
}
}
public void downloadLocalFile(String fileName, String path, String bucketName) {
try {
if (!this.client.doesBucketExist(bucketName)) {
throw new OssFileException("[阿里云OSS] 无法上传文件!Bucket不存在:" + bucketName);
}
// 下载Object到本地文件,并保存到指定的本地路径中。如果指定的本地文件存在会覆盖,不存在则新建。
// 如果未指定本地路径,则下载后的文件默认保存到示例程序所属项目对应本地路径中。
this.client.getObject(new GetObjectRequest(bucketName, fileName), new File(path));
} finally {
this.shutdown();
}
}
private void shutdown() {
this.client.shutdown();
}
}
3、由于我们自定义了Api,所以提供一个自定义的异常。
public class OssFileException extends RuntimeException {
public OssFileException(String message) {
super(message);
}
public OssFileException(String message, Throwable cause) {
super(message, cause);
}
}
4、因为需要把业务数据转为一个excel,但实际上我们在把数据转为 Workbook 的时候只需要获取到流就行了,所以需要把生成的 excel 转为流。
public class ExcelUtils {
public static InputStream workbookConvertorStream(Workbook workbook) {
InputStream in = null;
try {
//临时缓冲区
ByteArrayOutputStream out = new ByteArrayOutputStream();
//创建临时文件
workbook.write(out);
byte[] bookByteAry = out.toByteArray();
in = new ByteArrayInputStream(bookByteAry);
} catch (Exception e) {
e.printStackTrace();
}
return in;
}
}
测试
这里准备一个虚假数据来测试。
实体类:
import cn.afterturn.easypoi.excel.annotation.Excel;
import java.io.Serializable;
import java.util.Date;
public class StudentEntity implements Serializable {
private String id;
@Excel(name = "学生姓名", height = 20, width = 30, isimportField = "true_st")
private String name;
@Excel(name = "学生性别", replace = {"男_1", "女_2"}, suffix = "生", isimportField = "true_st")
private int sex;
@Excel(name = "出生日期", databaseFormat = "yyyyMMddHHmmss", format = "yyyy-MM-dd", isimportField = "true_st", width = 20)
private Date birthday;
@Excel(name = "进校日期", databaseFormat = "yyyyMMddHHmmss", format = "yyyy-MM-dd")
private Date registrationDate;
public StudentEntity() {
}
public StudentEntity(String id, String name, int sex, Date birthday, Date registrationDate) {
this.id = id;
this.name = name;
this.sex = sex;
this.birthday = birthday;
this.registrationDate = registrationDate;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Date getRegistrationDate() {
return registrationDate;
}
public void setRegistrationDate(Date registrationDate) {
this.registrationDate = registrationDate;
}
}
测试类:
public class TeatClient {
public static void main(String[] args) {
//模拟数据
List list = new ArrayList<>();
list.add(new StudentEntity("1", "张三", 18, new Date(), new Date()));
list.add(new StudentEntity("2", "李四", 19, new Date(), new Date()));
list.add(new StudentEntity("3", "王五", 20, new Date(), new Date()));
//HSSF_.xls, XSSF_.xlsx;
Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("计算机一班学生", "学生", "HSSF"), StudentEntity.class, list);
InputStream inputStream = ExcelUtils.workbookConvertorStream(workbook);
AliyunOssApiClient client = new AliyunOssApiClient().init();
String url = client.uploadFile(inputStream, SocialInsuranceAction.REGISTRATION.name().toLowerCase(), ".xls");
System.out.println(url);
}
}
测试结果:
返回一个文件连接,并且在阿里云控制台也能看到生成的文件。
https://wyh1995.oss-cn-hangzhou.aliyuncs.com/registration/20210926193659754.xls注意事项
在实际操作过程中,你可以按照自己需求去实现自己的工具类,主要参考OssApi这个类,这里讲讲提供的uploadFileExpired和uploadFile方法,其他的可自行查看,都有注释。
1、uploadFileExpired返回的是一个有过期时间的连接,并且是有权限才能访问的,在控制台详情可以看到:
其主要是通过OSSClient的授权方法实现的:
public String authFile(String fileName, String bucketName, long expirationTime) {
try {
if (!this.client.doesBucketExist(bucketName)) {
throw new OssApiException("[阿里云OSS] 无法授权访问文件的URL!Bucket不存在:" + bucketName);
}
if (!this.client.doesObjectExist(bucketName, fileName)) {
throw new OssApiException("[阿里云OSS] 文件授权失败!文件不存在:" + bucketName + "/" + fileName);
}
// 设置URL过期时间为1小时
Date expiration = new Date(new Date().getTime() + expirationTime * 1000);
// 生成URL
return this.client.generatePresignedUrl(bucketName, fileName, expiration).toString();
} finally {
this.shutdown();
}
}
2、uploadFile返回的是没有过期时间的连接,并且是开放权限的,任何人都可以访问,前提是你需要开启 Bucket权限:
如果你在没有开启的情况下调用该方法,结果可能就是这样:
还有一个需要注意的是,上面每个方法执行完成之后都是把OSSClient给关闭了,即调用了OssClient.shutdown(),相当于连接关闭,所以你不能连续的执行不同方法,除非你在shutdown之前,不然会出现以下情况。