文件上传

背景

在实际开发中,我们经常会使用到文件上传功能,特在此总结常用的文件上传,我们常用的长传是阿里云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);
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值