背景
在实际开发中,我们经常会使用到文件上传功能,特在此总结常用的文件上传,我们常用的长传是阿里云OSS、七牛云、自建(MinIO,FastDFS等)
方式
代码
代码结构外部包
<properties>
<spring.plugin.version>2.0.0.RELEASE</spring.plugin.version>
<spring.boot.version>2.4.1</spring.boot.version>
<spring.cloud.version>2020.0.0</spring.cloud.version>
<qiniu.oss.version>7.2.18</qiniu.oss.version>
<ali.oss.version>3.1.0</ali.oss.version>
<ali.pay.version>4.10.97.ALL</ali.pay.version>
<min.io.version>3.0.10</min.io.version>
<lombok.version>1.18.12</lombok.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
<version>${spring.plugin.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-metadata</artifactId>
<version>${spring.plugin.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>sharp-weapon</groupId>
<artifactId>core-tool</artifactId>
<version>${sharp.weapon.core.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--七牛云-->
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>${qiniu.oss.version}</version>
</dependency>
<!--阿里云oss-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${ali.oss.version}</version>
</dependency>
<!-- MinIO -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${min.io.version}</version>
</dependency>
</dependencies>
实体定义
文件资源
package org.weapon.third.file.model;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 文件资源
*
* @author lieber
*/
@Data
public class FileResource {
/**
* 文件地址
*/
private String link;
/**
* 文件名
*/
private String name;
/**
* 文件hash值
*/
public String hash;
/**
* 文件大小
*/
private long length;
/**
* 文件上传时间
*/
private LocalDateTime putTime;
/**
* 文件contentType
*/
private String contentType;
}
文件上传结果
package org.weapon.third.file.model;
import lombok.Builder;
import lombok.Data;
/**
* 文件上传结果
*
* @author lieber
*/
@Data
@Builder
public class FileUploadResult {
/**
* 文件地址
*/
private String path;
/**
* 文件名
*/
private String name;
/**
* 原始文件名
*/
private String originalName;
}
参数配置
package org.weapon.third.file.props;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 参数配置类
*
* @author lieber
*/
@Data
@ConfigurationProperties(prefix = "weapon.file")
public class FileProperties {
/**
* 是否启用
*/
private Boolean enabled;
/**
* 对象存储名称
*/
private String name;
/**
* 对象存储服务的URL
*/
private String endpoint;
/**
* Access key就像用户ID,可以唯一标识你的账户
*/
private String accessKey;
/**
* Secret key是你账户的密码
*/
private String secretKey;
/**
* 默认的存储桶名称
*/
private String bucketName;
private long expireTime = 3600L;
private long contentLengthRange = 10485760;
}
通用规则
通用规则接口定义
package org.weapon.third.file.rule;
/**
* 通用规则
*
* @author lieber
*/
public interface IFileRule {
/**
* 获取存储桶规则
*
* @param bucketName 存储桶名称
* @return String
*/
String bucketName(String bucketName);
/**
* 获取文件名规则
*
* @param originalFilename 文件名
* @return String
*/
String fileName(String originalFilename);
}
默认规则实现
package org.weapon.third.file.rule;
import org.weapon.core.tool.DateTimeUtil;
import org.weapon.core.tool.FileUtil;
import org.weapon.core.tool.Strings;
/**
* 默认存储桶生成规则
*
* @author lieber
*/
public class DefaultFileRule implements IFileRule {
@Override
public String bucketName(String bucketName) {
return bucketName;
}
@Override
public String fileName(String originalFilename) {
return "upload" + Strings.FILE_SLASH +
DateTimeUtil.today() + Strings.FILE_SLASH +
Strings.randomUUID() + Strings.DOT + FileUtil.getFileExtension(originalFilename);
}
}
文件上传
文件上传接口
package org.weapon.third.file.template;
import org.springframework.web.multipart.MultipartFile;
import org.weapon.third.file.model.FileResource;
import org.weapon.third.file.model.FileUploadResult;
import java.io.InputStream;
import java.util.List;
/**
* @author lieber
*/
public interface IFileTemplate {
/**
* 创建bucket
*
* @param bucketName bucket名称
*/
void createBucket(String bucketName);
/**
* 删除bucket
*
* @param bucketName bucket名称
*/
void removeBucket(String bucketName);
/**
* bucket是否存在
*
* @param bucketName bucket名称
* @return 存在为true
*/
boolean bucketExists(String bucketName);
/**
* 文件拷贝
*
* @param bucketName bucket名称
* @param fileName 文件名
* @param destinationBucketName 目标文件名
*/
void copyFile(String bucketName, String fileName, String destinationBucketName);
/**
* 文件拷贝
*
* @param bucketName 原bucket名称
* @param fileName 原文件名
* @param destinationBucketName 目标bucket名称
* @param destinationFileName 目标文件名
*/
void copyFile(String bucketName, String fileName, String destinationBucketName, String destinationFileName);
/**
* 获取文件信息
*
* @param fileName 文件名
* @return 文件信息
*/
FileResource statFile(String fileName);
/**
* 获取文件信息
*
* @param bucketName bucket名称
* @param fileName 文件名
* @return 文件信息
*/
FileResource statFile(String bucketName, String fileName);
/**
* 获取文件路径
*
* @param fileName 文件名
* @return 文件路径
*/
String filePath(String fileName);
/**
* 获取文件路径
*
* @param bucketName bucket名称
* @param fileName 文件名
* @return 文件路径
*/
String filePath(String bucketName, String fileName);
/**
* 获取文件链接
*
* @param fileName 文件名
* @return 件链接
*/
String fileLink(String fileName);
/**
* 取文件链接
*
* @param bucketName bucket名称
* @param fileName 文件名
* @return 件链接
*/
String fileLink(String bucketName, String fileName);
/**
* 文件上传
*
* @param file 文件
* @return 上传结果
*/
FileUploadResult putFile(MultipartFile file);
/**
* 文件上传
*
* @param fileName 文件名
* @param file 文件
* @return 上传结果
*/
FileUploadResult putFile(String fileName, MultipartFile file);
/**
* 文件上传
*
* @param bucketName bucket名称
* @param fileName 文件名
* @param file 文件
* @return 上传结果
*/
FileUploadResult putFile(String bucketName, String fileName, MultipartFile file);
/**
* 文件上传
*
* @param fileName 文件名
* @param stream 文件流
* @return 上传结果
*/
FileUploadResult putFile(String fileName, InputStream stream);
/**
* 文件上传
*
* @param bucketName bucket名称
* @param fileName 文件名
* @param stream 文件流
* @return 上传结果
*/
FileUploadResult putFile(String bucketName, String fileName, InputStream stream);
/**
* 文件上传
*
* @param bucketName bucket名称
* @param stream 文件流
* @param fileName 文件名
* @param cover 是否覆盖上传
* @return 上传结果
*/
FileUploadResult put(String bucketName, InputStream stream, String fileName, boolean cover);
/**
* 删除文件
*
* @param fileName 文件名
*/
void removeFile(String fileName);
/**
* 删除文件
*
* @param bucketName bucket名称
* @param fileName 文件名
*/
void removeFile(String bucketName, String fileName);
/**
* 批量删除文件
*
* @param fileNames 文件名集合
*/
void removeFiles(List<String> fileNames);
/**
* 批量删除文件
*
* @param bucketName bucket名称
* @param fileNames 文件名集合
*/
void removeFiles(String bucketName, List<String> fileNames);
/**
* 上传凭证
*
* @return 上传凭证
*/
String getUploadToken();
/**
* 上传凭证
*
* @param bucketName bucket名称
* @return 上传凭证
*/
String getUploadToken(String bucketName);
}
实现
阿里云OSS文件上传接口实现
package org.weapon.third.file.template;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PolicyConditions;
import com.aliyun.oss.model.PutObjectResult;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import org.weapon.third.file.model.FileResource;
import org.weapon.third.file.model.FileUploadResult;
import org.weapon.third.file.props.FileProperties;
import org.weapon.third.file.rule.IFileRule;
import org.weapon.core.tool.DateTimeUtil;
import org.weapon.core.tool.JsonUtil;
import org.weapon.core.tool.Strings;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* 阿里云OSS文件上传
*
* @author lieber
*/
@AllArgsConstructor
public class AliOssTemplate implements IFileTemplate {
private OSSClient client;
private FileProperties properties;
private IFileRule rule;
@Override
public void createBucket(String bucketName) {
if (!bucketExists(bucketName)) {
client.createBucket(getBucketName(bucketName));
}
}
@Override
public void removeBucket(String bucketName) {
client.deleteBucket(getBucketName(bucketName));
}
@Override
public boolean bucketExists(String bucketName) {
return client.doesBucketExist(getBucketName(bucketName));
}
@Override
public void copyFile(String bucketName, String fileName, String destinationBucketName) {
client.copyObject(getBucketName(bucketName), fileName, getBucketName(destinationBucketName), fileName);
}
@Override
public void copyFile(String bucketName, String fileName, String destinationBucketName, String destinationFileName) {
client.copyObject(getBucketName(bucketName), fileName, getBucketName(destinationBucketName), destinationFileName);
}
@Override
public FileResource statFile(String fileName) {
return statFile(properties.getBucketName(), fileName);
}
@Override
public FileResource statFile(String bucketName, String fileName) {
ObjectMetadata stat = client.getObjectMetadata(getBucketName(bucketName), fileName);
FileResource fileResource = new FileResource();
fileResource.setName(fileName);
fileResource.setLink(fileLink(fileResource.getName()));
fileResource.setHash(stat.getContentMD5());
fileResource.setLength(stat.getContentLength());
fileResource.setPutTime(DateTimeUtil.toDateTime(stat.getLastModified()));
fileResource.setContentType(stat.getContentType());
return fileResource;
}
@Override
public String filePath(String fileName) {
return this.getHost().concat(Strings.FILE_SLASH).concat(fileName);
}
@Override
public String filePath(String bucketName, String fileName) {
return this.getHost(bucketName).concat(Strings.FILE_SLASH).concat(fileName);
}
@Override
public String fileLink(String fileName) {
return this.getHost().concat(Strings.FILE_SLASH).concat(fileName);
}
@Override
public String fileLink(String bucketName, String fileName) {
return this.getHost(bucketName).concat(Strings.FILE_SLASH).concat(fileName);
}
@Override
@SneakyThrows
public FileUploadResult putFile(MultipartFile file) {
return putFile(properties.getBucketName(), file.getOriginalFilename(), file);
}
@Override
@SneakyThrows
public FileUploadResult putFile(String fileName, MultipartFile file) {
return putFile(properties.getBucketName(), fileName, file);
}
@Override
@SneakyThrows
public FileUploadResult putFile(String bucketName, String fileName, MultipartFile file) {
return putFile(bucketName, fileName, file.getInputStream());
}
@Override
public FileUploadResult putFile(String fileName, InputStream stream) {
return putFile(properties.getBucketName(), fileName, stream);
}
@Override
public FileUploadResult putFile(String bucketName, String fileName, InputStream stream) {
return put(bucketName, stream, fileName, false);
}
@Override
public FileUploadResult put(String bucketName, InputStream stream, String originalName, boolean cover) {
if (!this.bucketExists(bucketName)) {
this.createBucket(bucketName);
}
String key = getFileName(originalName);
// 覆盖上传
if (cover) {
client.putObject(getBucketName(bucketName), key, stream);
} else {
PutObjectResult response = client.putObject(getBucketName(bucketName), key, stream);
int retry = 0;
int retryCount = 5;
while (StringUtils.hasText(response.getETag()) && retry < retryCount) {
response = client.putObject(getBucketName(bucketName), key, stream);
retry++;
}
}
return FileUploadResult.builder().name(key).originalName(originalName).path(this.fileLink(bucketName, key)).build();
}
@Override
public void removeFile(String fileName) {
client.deleteObject(getBucketName(), fileName);
}
@Override
public void removeFile(String bucketName, String fileName) {
client.deleteObject(getBucketName(bucketName), fileName);
}
@Override
public void removeFiles(List<String> fileNames) {
fileNames.forEach(this::removeFile);
}
@Override
public void removeFiles(String bucketName, List<String> fileNames) {
fileNames.forEach(fileName -> removeFile(getBucketName(bucketName), fileName));
}
@Override
public String getUploadToken() {
return this.getUploadToken(properties.getBucketName());
}
@Override
public String getUploadToken(String bucketName) {
return this.getUploadToken(bucketName, properties.getExpireTime());
}
private String getUploadToken(String bucketName, long expireTime) {
String baseDir = "upload";
long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
Date expiration = new Date(expireEndTime);
PolicyConditions policyConditions = new PolicyConditions();
// 默认大小限制10M
policyConditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, properties.getContentLengthRange());
policyConditions.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, baseDir);
String postPolicy = client.generatePostPolicy(expiration, policyConditions);
byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = client.calculatePostSignature(postPolicy);
Map<String, String> respMap = new LinkedHashMap<>(16);
respMap.put("accessid", properties.getAccessKey());
respMap.put("policy", encodedPolicy);
respMap.put("signature", postSignature);
respMap.put("dir", baseDir);
respMap.put("host", this.getHost(bucketName));
respMap.put("expire", String.valueOf(expireEndTime / 1000));
return JsonUtil.toJsonString(respMap).orElse(Strings.EMPTY);
}
private String getBucketName() {
return getBucketName(properties.getBucketName());
}
private String getHost(String bucketName) {
String prefix = properties.getEndpoint().contains("https://") ? "https://" : "http://";
return prefix + getBucketName(bucketName) + Strings.DOT + properties.getEndpoint().replaceFirst(prefix, Strings.EMPTY);
}
private String getHost() {
return this.getHost(properties.getBucketName());
}
private String getBucketName(String bucketName) {
return rule.bucketName(bucketName);
}
private String getFileName(String originalFilename) {
return rule.fileName(originalFilename);
}
}
七牛云文件上传接口实现
package org.weapon.third.file.template;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.FileInfo;
import com.qiniu.util.Auth;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.web.multipart.MultipartFile;
import org.weapon.third.file.model.FileResource;
import org.weapon.third.file.model.FileUploadResult;
import org.weapon.third.file.props.FileProperties;
import org.weapon.third.file.rule.IFileRule;
import org.weapon.core.tool.CollectionUtil;
import org.weapon.core.tool.DateTimeUtil;
import org.weapon.core.tool.Strings;
import java.io.InputStream;
import java.util.List;
/**
* 七牛云文件上传
*
* @author lieber
*/
@AllArgsConstructor
public class QiNiuTemplate implements IFileTemplate {
private Auth auth;
private UploadManager uploadManager;
private BucketManager bucketManager;
private FileProperties properties;
private IFileRule rule;
@Override
@SneakyThrows
public void createBucket(String bucketName) {
if (!CollectionUtil.contains(this.bucketManager.buckets(), getBucketName(bucketName))) {
this.bucketManager.createBucket(getBucketName(bucketName), Zone.zone0().getRegion());
}
}
@Override
@SneakyThrows
public void removeBucket(String bucketName) {
this.bucketManager.deleteBucket(getBucketName(bucketName));
}
@Override
@SneakyThrows
public boolean bucketExists(String bucketName) {
return CollectionUtil.contains(this.bucketManager.buckets(), getBucketName(bucketName));
}
@Override
@SneakyThrows
public void copyFile(String bucketName, String fileName, String destinationBucketName) {
this.bucketManager.copy(getBucketName(bucketName), fileName, getBucketName(destinationBucketName), fileName);
}
@Override
@SneakyThrows
public void copyFile(String bucketName, String fileName, String destinationBucketName, String destinationFileName) {
this.bucketManager.copy(getBucketName(bucketName), fileName, getBucketName(destinationBucketName), destinationFileName);
}
@Override
public FileResource statFile(String fileName) {
return statFile(this.properties.getBucketName(), fileName);
}
@Override
@SneakyThrows
public FileResource statFile(String bucketName, String fileName) {
FileInfo stat = this.bucketManager.stat(getBucketName(bucketName), fileName);
FileResource ossFile = new FileResource();
ossFile.setName(stat.key);
ossFile.setLink(fileLink(ossFile.getName()));
ossFile.setHash(stat.hash);
ossFile.setLength(stat.fsize);
ossFile.setPutTime(DateTimeUtil.formTimestamp(stat.putTime / 10000));
ossFile.setContentType(stat.mimeType);
return ossFile;
}
@Override
public String filePath(String fileName) {
return getBucketName().concat(Strings.FILE_SLASH).concat(fileName);
}
@Override
public String filePath(String bucketName, String fileName) {
return getBucketName(bucketName).concat(Strings.FILE_SLASH).concat(fileName);
}
@Override
public String fileLink(String fileName) {
return properties.getEndpoint().concat(Strings.FILE_SLASH).concat(fileName);
}
@Override
public String fileLink(String bucketName, String fileName) {
return properties.getEndpoint().concat(Strings.FILE_SLASH).concat(fileName);
}
@Override
public FileUploadResult putFile(MultipartFile file) {
return putFile(properties.getBucketName(), file.getOriginalFilename(), file);
}
@Override
public FileUploadResult putFile(String fileName, MultipartFile file) {
return putFile(properties.getBucketName(), fileName, file);
}
@Override
@SneakyThrows
public FileUploadResult putFile(String bucketName, String fileName, MultipartFile file) {
return putFile(bucketName, fileName, file.getInputStream());
}
@Override
public FileUploadResult putFile(String fileName, InputStream stream) {
return putFile(properties.getBucketName(), fileName, stream);
}
@Override
public FileUploadResult putFile(String bucketName, String fileName, InputStream stream) {
return put(bucketName, stream, fileName, false);
}
@Override
@SneakyThrows
public FileUploadResult put(String bucketName, InputStream stream, String fileName, boolean cover) {
if (this.bucketExists(bucketName)) {
this.createBucket(bucketName);
}
String key = getFileName(fileName);
// 覆盖上传
if (cover) {
this.uploadManager.put(stream, key, getUploadToken(bucketName, key), null, null);
} else {
Response response = this.uploadManager.put(stream, key, getUploadToken(bucketName), null, null);
int retry = 0;
int retryCount = 5;
while (response.needRetry() && retry < retryCount) {
response = this.uploadManager.put(stream, key, getUploadToken(bucketName), null, null);
retry++;
}
}
return FileUploadResult.builder().name(key).originalName(fileName).path(fileLink(bucketName, key)).build();
}
@Override
@SneakyThrows
public void removeFile(String fileName) {
bucketManager.delete(getBucketName(), fileName);
}
@Override
@SneakyThrows
public void removeFile(String bucketName, String fileName) {
bucketManager.delete(getBucketName(bucketName), fileName);
}
@Override
public void removeFiles(List<String> fileNames) {
fileNames.forEach(this::removeFile);
}
@Override
public void removeFiles(String bucketName, List<String> fileNames) {
fileNames.forEach(fileName -> removeFile(getBucketName(bucketName), fileName));
}
@Override
public String getUploadToken() {
return this.getUploadToken(this.properties.getBucketName());
}
/**
* 根据规则生成存储桶名称规则
*
* @return String
*/
private String getBucketName() {
return getBucketName(this.properties.getBucketName());
}
/**
* 根据规则生成存储桶名称规则
*
* @param bucketName 存储桶名称
* @return String
*/
private String getBucketName(String bucketName) {
return this.rule.bucketName(bucketName);
}
/**
* 根据规则生成文件名称规则
*
* @param originalFilename 原始文件名
* @return string
*/
private String getFileName(String originalFilename) {
return this.rule.fileName(originalFilename);
}
/**
* 获取上传凭证,普通上传
*/
@Override
public String getUploadToken(String bucketName) {
return this.auth.uploadToken(this.getBucketName(bucketName));
}
/**
* 获取上传凭证,覆盖上传
*/
private String getUploadToken(String bucketName, String key) {
return this.auth.uploadToken(this.getBucketName(bucketName), key);
}
}
MinIO上传接口实现
package org.weapon.third.file.template;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.web.multipart.MultipartFile;
import org.weapon.core.tool.DateTimeUtil;
import org.weapon.core.tool.Strings;
import org.weapon.third.file.model.FileResource;
import org.weapon.third.file.model.FileUploadResult;
import org.weapon.third.file.props.FileProperties;
import org.weapon.third.file.rule.IFileRule;
import java.io.InputStream;
import java.util.List;
/**
* MinIO文件上传
*
* @author lieber
*/
@AllArgsConstructor
public class MinIoTemplate implements IFileTemplate {
private MinioClient client;
private FileProperties properties;
private IFileRule rule;
@SneakyThrows
@Override
public void createBucket(String bucketName) {
if (!bucketExists(bucketName)) {
client.makeBucket(getBucketName(bucketName));
}
}
@SneakyThrows
@Override
public void removeBucket(String bucketName) {
client.removeBucket(getBucketName(bucketName));
}
@SneakyThrows
@Override
public boolean bucketExists(String bucketName) {
return client.bucketExists(getBucketName(bucketName));
}
@SneakyThrows
@Override
public void copyFile(String bucketName, String fileName, String destinationBucketName) {
client.copyObject(getBucketName(bucketName), fileName, getBucketName(destinationBucketName), fileName);
}
@SneakyThrows
@Override
public void copyFile(String bucketName, String fileName, String destinationBucketName, String destinationFileName) {
client.copyObject(getBucketName(bucketName), fileName, getBucketName(destinationBucketName), destinationFileName);
}
@Override
public FileResource statFile(String fileName) {
return statFile(properties.getBucketName(), fileName);
}
@SneakyThrows
@Override
public FileResource statFile(String bucketName, String fileName) {
ObjectStat stat = client.statObject(getBucketName(bucketName), fileName);
FileResource fileResource = new FileResource();
fileResource.setName(fileName);
fileResource.setLink(fileLink(fileResource.getName()));
fileResource.setHash(null);
fileResource.setLength(stat.length());
fileResource.setPutTime(DateTimeUtil.toDateTime(stat.createdTime()));
fileResource.setContentType(stat.contentType());
return fileResource;
}
@Override
public String filePath(String fileName) {
return this.getHost().concat(Strings.FILE_SLASH).concat(fileName);
}
@Override
public String filePath(String bucketName, String fileName) {
return this.getHost(bucketName).concat(Strings.FILE_SLASH).concat(fileName);
}
@Override
public String fileLink(String fileName) {
return this.getHost().concat(Strings.FILE_SLASH).concat(fileName);
}
@Override
public String fileLink(String bucketName, String fileName) {
return this.getHost(bucketName).concat(Strings.FILE_SLASH).concat(fileName);
}
@Override
@SneakyThrows
public FileUploadResult putFile(MultipartFile file) {
return putFile(properties.getBucketName(), file.getOriginalFilename(), file);
}
@Override
@SneakyThrows
public FileUploadResult putFile(String fileName, MultipartFile file) {
return putFile(properties.getBucketName(), fileName, file);
}
@Override
@SneakyThrows
public FileUploadResult putFile(String bucketName, String fileName, MultipartFile file) {
return putFile(bucketName, fileName, file.getInputStream());
}
@Override
public FileUploadResult putFile(String fileName, InputStream stream) {
return putFile(properties.getBucketName(), fileName, stream);
}
@Override
public FileUploadResult putFile(String bucketName, String fileName, InputStream stream) {
return put(bucketName, stream, fileName, false);
}
@Override
@SneakyThrows
public FileUploadResult put(String bucketName, InputStream stream, String originalName, boolean cover) {
if (!this.bucketExists(bucketName)) {
this.createBucket(bucketName);
}
String key = getFileName(originalName);
// 覆盖上传
if (cover) {
client.putObject(getBucketName(bucketName), key, stream, stream.available(), "application/octet-stream");
} else {
boolean success = false;
int retry = 0;
int retryCount = 5;
while (!success && retry < retryCount) {
try {
client.putObject(getBucketName(bucketName), key, stream, stream.available(), "application/octet-stream");
success = true;
} catch (Exception ignore) {
}
retry++;
}
}
return FileUploadResult.builder().name(key).originalName(originalName).path(this.fileLink(bucketName, key)).build();
}
@Override
@SneakyThrows
public void removeFile(String fileName) {
client.removeObject(getBucketName(), fileName);
}
@Override
@SneakyThrows
public void removeFile(String bucketName, String fileName) {
client.removeObject(getBucketName(bucketName), fileName);
}
@Override
public void removeFiles(List<String> fileNames) {
fileNames.forEach(this::removeFile);
}
@Override
public void removeFiles(String bucketName, List<String> fileNames) {
fileNames.forEach(fileName -> removeFile(getBucketName(bucketName), fileName));
}
@Override
public String getUploadToken() {
return this.getUploadToken(properties.getBucketName());
}
@Override
public String getUploadToken(String bucketName) {
throw new IllegalArgumentException("不支持的方式");
}
private String getBucketName() {
return getBucketName(properties.getBucketName());
}
private String getHost(String bucketName) {
String prefix = properties.getEndpoint().contains("https://") ? "https://" : "http://";
return prefix + properties.getEndpoint().replaceFirst(prefix, Strings.EMPTY) + Strings.FILE_SLASH + getBucketName(bucketName);
}
private String getHost() {
return this.getHost(properties.getBucketName());
}
private String getBucketName(String bucketName) {
return rule.bucketName(bucketName);
}
private String getFileName(String originalFilename) {
return rule.fileName(originalFilename);
}
}
使用配置
阿里OSS配置类
package org.weapon.third.file.config;
import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.auth.CredentialsProvider;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.weapon.third.file.template.AliOssTemplate;
import org.weapon.third.file.template.IFileTemplate;
import org.weapon.third.file.props.FileProperties;
import org.weapon.third.file.rule.DefaultFileRule;
import org.weapon.third.file.rule.IFileRule;
/**
* 阿里OSS配置类
*
* @author lieber
*/
@Configuration(proxyBeanMethods = false)
@AllArgsConstructor
@AutoConfigureAfter(QiNiuConfiguration.class)
@EnableConfigurationProperties(FileProperties.class)
@ConditionalOnProperty(value = "weapon.file.name", havingValue = "ali-oss")
public class AliOssConfiguration {
private FileProperties ossProperties;
@Bean
@ConditionalOnMissingBean(IFileRule.class)
public IFileRule ossRule() {
return new DefaultFileRule();
}
@Bean
@ConditionalOnMissingBean(OSSClient.class)
public OSSClient ossClient() {
// 创建ClientConfiguration。ClientConfiguration是OSSClient的配置类,可配置代理、连接超时、最大连接数等参数。
ClientConfiguration conf = new ClientConfiguration();
// 设置OSSClient允许打开的最大HTTP连接数,默认为1024个。
conf.setMaxConnections(1024);
// 设置Socket层传输数据的超时时间,默认为50000毫秒。
conf.setSocketTimeout(50000);
// 设置建立连接的超时时间,默认为50000毫秒。
conf.setConnectionTimeout(50000);
// 设置从连接池中获取连接的超时时间(单位:毫秒),默认不超时。
conf.setConnectionRequestTimeout(1000);
// 设置连接空闲超时时间。超时则关闭连接,默认为60000毫秒。
conf.setIdleConnectionTime(60000);
// 设置失败请求重试次数,默认为3次。
conf.setMaxErrorRetry(5);
CredentialsProvider credentialsProvider = new DefaultCredentialProvider(ossProperties.getAccessKey(), ossProperties.getSecretKey());
return new OSSClient(ossProperties.getEndpoint(), credentialsProvider, conf);
}
@Bean
@ConditionalOnMissingBean(IFileTemplate.class)
@ConditionalOnBean({OSSClient.class, IFileRule.class})
public IFileTemplate aliOssTemplate(OSSClient ossClient, IFileRule fileRule) {
return new AliOssTemplate(ossClient, ossProperties, fileRule);
}
}
七牛配置类
package org.weapon.third.file.config;
import com.qiniu.common.Zone;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.UploadManager;
import com.qiniu.util.Auth;
import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.weapon.third.file.props.FileProperties;
import org.weapon.third.file.rule.DefaultFileRule;
import org.weapon.third.file.rule.IFileRule;
import org.weapon.third.file.template.IFileTemplate;
import org.weapon.third.file.template.QiNiuTemplate;
/**
* 七牛配置类
*
* @author lieber
*/
@Configuration(proxyBeanMethods = false)
@AllArgsConstructor
@EnableConfigurationProperties(FileProperties.class)
@ConditionalOnProperty(value = "weapon.file.name", havingValue = "qi-niu")
public class QiNiuConfiguration {
private FileProperties properties;
@Bean
@ConditionalOnMissingBean(IFileRule.class)
public IFileRule ossRule() {
return new DefaultFileRule();
}
@Bean
public com.qiniu.storage.Configuration qiNiuConfiguration() {
return new com.qiniu.storage.Configuration(Zone.zone0());
}
@Bean
public Auth auth() {
return Auth.create(properties.getAccessKey(), properties.getSecretKey());
}
@Bean
@ConditionalOnBean(com.qiniu.storage.Configuration.class)
public UploadManager uploadManager(com.qiniu.storage.Configuration cfg) {
return new UploadManager(cfg);
}
@Bean
@ConditionalOnBean(com.qiniu.storage.Configuration.class)
public BucketManager bucketManager(com.qiniu.storage.Configuration cfg) {
return new BucketManager(auth(), cfg);
}
@Bean
@ConditionalOnMissingBean(IFileTemplate.class)
@ConditionalOnBean({Auth.class, UploadManager.class, BucketManager.class, IFileRule.class})
public IFileTemplate qiNiuTemplate(Auth auth, UploadManager uploadManager, BucketManager bucketManager, IFileRule rule) {
return new QiNiuTemplate(auth, uploadManager, bucketManager, properties, rule);
}
}
MinIO配置类
package org.weapon.third.file.config;
import io.minio.MinioClient;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.weapon.third.file.props.FileProperties;
import org.weapon.third.file.rule.DefaultFileRule;
import org.weapon.third.file.rule.IFileRule;
import org.weapon.third.file.template.IFileTemplate;
import org.weapon.third.file.template.MinIoTemplate;
/**
* 阿里OSS配置类
*
* @author lieber
*/
@Configuration
@AllArgsConstructor
@AutoConfigureAfter(MinIoConfiguration.class)
@EnableConfigurationProperties(FileProperties.class)
@ConditionalOnProperty(value = "weapon.file.name", havingValue = "min-io")
public class MinIoConfiguration {
private FileProperties minIoProperties;
@Bean
@ConditionalOnMissingBean(IFileRule.class)
public IFileRule ossRule() {
return new DefaultFileRule();
}
@Bean
@ConditionalOnMissingBean(MinioClient.class)
@SneakyThrows
public MinioClient minioClient() {
return new MinioClient(minIoProperties.getEndpoint(), minIoProperties.getAccessKey(), minIoProperties.getSecretKey());
}
@Bean
@ConditionalOnMissingBean(IFileTemplate.class)
@ConditionalOnBean({MinioClient.class, IFileRule.class})
public IFileTemplate aliOssTemplate(MinioClient minioClient, IFileRule fileRule) {
return new MinIoTemplate(minioClient, minIoProperties, fileRule);
}
}
网友评论