minio&前后端分离上传视频/上传大文件——前后端分离断点续传&minio分片上传实现(二)

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: minio&前后端分离上传视频/上传大文件——前后端分离断点续传&minio分片上传实现

5.5 MinioClientUtils工具类

package com.zhulang.waveedu.common.util;
import com.j256.simplemagic.ContentInfo;
import com.j256.simplemagic.ContentInfoUtil;
import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.UploadObjectArgs;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.http.MediaType;
import java.io.*;
/**
 * 操作minio的工具类
 *
 * @author 狐狸半面添
 * @create 2023-02-08 22:08
 */
public class MinioClientUtils {
    private final MinioClient minioClient;
    public MinioClientUtils(MinioClient minioClient) {
        this.minioClient = minioClient;
    }
    /**
     * 获取minio文件的输入流对象
     *
     * @param bucket   桶
     * @param filePath 文件路径
     * @return 输入流
     * @throws Exception 异常
     */
    public InputStream getObject(String bucket, String filePath) throws Exception {
        return minioClient.getObject(GetObjectArgs.builder().bucket(bucket).object(filePath).build());
    }
    /**
     * 将分块文件上传到分布式文件系统
     *
     * @param bytes    文件的字节数组
     * @param bucket   桶
     * @param filePath 存储在桶中的文件路径
     */
    public void uploadChunkFile(byte[] bytes, String bucket, String filePath) throws Exception {
        // 1.指定资源的媒体类型为未知二进制流,以分片形式上传至minio
        try (
                ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes)
        ) {
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucket)
                            .object(filePath)
                            // InputStream stream, long objectSize 对象大小, long partSize 分片大小(-1表示5M,最大不要超过5T,最多10000)
                            .stream(byteArrayInputStream, byteArrayInputStream.available(), -1)
                            .contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE)
                            .build()
            );
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 将文件上传到分布式文件系统
     *
     * @param bytes    文件的字节数组
     * @param bucket   桶
     * @param filePath 存储在桶中的文件路径
     */
    public void uploadFile(byte[] bytes, String bucket, String filePath) throws Exception {
        // 1.指定资源的媒体类型,默认未知二进制流
        String contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
        // 2.判断是否有后缀,有后缀则根据后缀推算出文件类型,否则使用默认的未知二进制流
        if (filePath.contains(".")) {
            // 取objectName中的扩展名
            String extension = filePath.substring(filePath.lastIndexOf("."));
            ContentInfo extensionMatch = ContentInfoUtil.findExtensionMatch(extension);
            if (extensionMatch != null) {
                contentType = extensionMatch.getMimeType();
            }
        }
        // 3.以分片形式上传至minio
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                .bucket(bucket)
                .object(filePath)
                // InputStream stream, long objectSize 对象大小, long partSize 分片大小(-1表示5M,最大不要超过5T,最多10000)
                .stream(byteArrayInputStream, byteArrayInputStream.available(), -1)
                .contentType(contentType)
                .build();
        // 上传
        minioClient.putObject(putObjectArgs);
    }
    /**
     * 根据文件路径将文件上传到文件系统
     *
     * @param naiveFilePath 本地文件路径
     * @param bucket        桶
     * @param minioFilePath 保存到minio的文件路径位置
     * @throws Exception 异常
     */
    public void uploadChunkFile(String naiveFilePath, String bucket, String minioFilePath) throws Exception {
        UploadObjectArgs uploadObjectArgs = UploadObjectArgs.builder()
                .bucket(bucket)
                .object(minioFilePath)
                .filename(naiveFilePath)
                .build();
        minioClient.uploadObject(uploadObjectArgs);
    }
    /**
     * 下载文件保存至本地临时文件中
     *
     * @param tempFilePrefix 临时文件的前缀
     * @param tempFileSuffix 临时文件的后缀
     * @param bucket         桶
     * @param filePath       文件路径
     * @return 携带数据的临时文件
     * @throws Exception 异常信息
     */
    public File downloadFile(String tempFilePrefix, String tempFileSuffix, String bucket, String filePath) throws Exception {
        // 1.创建空文件,临时保存下载下来的分块文件数据
        File tempFile = File.createTempFile(tempFilePrefix, tempFileSuffix);
        try (
                // 2.获取目标文件的输入流对象
                InputStream inputStream = getObject(bucket, filePath);
                // 3.获取临时空文件的输出流对象
                FileOutputStream outputStream = new FileOutputStream(tempFile);
        ) {
            // 4.进行数据拷贝
            IOUtils.copy(inputStream, outputStream);
            // 5.返回保存了数据的临时文件
            return tempFile;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }
//    public File downloadFile(String tempFilePrefix, String tempFileSuffix, String bucket, String filePath) throws Exception {
//        // 1.创建空文件,临时保存下载下来的分块文件数据
//        File tempFile = File.createTempFile(tempFilePrefix, tempFileSuffix);
//        try {
//            Long start = System.currentTimeMillis();
//            minioClient.downloadObject(
//                    DownloadObjectArgs.builder()
//                            // 指定 bucket 存储桶
//                            .bucket(bucket)
//                            // 指定 哪个文件
//                            .object(filePath)
//                            // 指定存放位置与名称
//                            .filename(tempFile.getPath())
//                            .build());
//            Long end = System.currentTimeMillis();
//            System.out.println("下载分块时间:"+(end-start)+"ms");
//            // 5.返回保存了数据的临时文件
//            return tempFile;
//        } catch (Exception e) {
//            throw new RuntimeException(e.getMessage());
//        }
//    }
}

5.6 FileTypeUtils工具类

package com.zhulang.waveedu.common.util;
import org.apache.tika.metadata.HttpHeaders;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.TikaCoreProperties;
import org.apache.tika.mime.MediaType;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.Parser;
import org.xml.sax.helpers.DefaultHandler;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
/**
 * 文件类型工具类
 *
 * @author 狐狸半面添
 * @create 2023-02-09 0:13
 */
public class FileTypeUtils {
    private static final Map<String, String> contentType = new HashMap<>();
    /**
     * 获取文件的 mime 类型
     *
     * @param file 文件
     * @return mime类型
     */
    public static String getMimeType(File file) {
        AutoDetectParser parser = new AutoDetectParser();
        parser.setParsers(new HashMap<MediaType, Parser>());
        Metadata metadata = new Metadata();
        metadata.add(TikaCoreProperties.RESOURCE_NAME_KEY, file.getName());
        try (InputStream stream = Files.newInputStream(file.toPath())) {
            parser.parse(stream, new DefaultHandler(), metadata, new ParseContext());
        } catch (Exception e) {
            throw new RuntimeException();
        }
        return metadata.get(HttpHeaders.CONTENT_TYPE);
    }
    /**
     * 根据 mimetype 获取文件的简单类型
     *
     * @param mimeType mime类型
     * @return 简单类型:文本,图片,音频,视频,其它
     */
    public static String getSimpleType(String mimeType) {
        String simpleType = mimeType.split("/")[0];
        switch (simpleType) {
            case "text":
                return "文本";
            case "image":
                return "图片";
            case "audio":
                return "音频";
            case "video":
                return "视频";
            case "application":
                return "其它";
            default:
                throw new RuntimeException("mimeType格式错误");
        }
    }
    // 测试
    public static void main(String[] args) {
        File file = new File("D:\\location语法规则.docx");
        String mimeType = getMimeType(file);
        System.out.println(mimeType);
        System.out.println(getSimpleType(mimeType));
    }

5.7 FileFormatUtils工具类

package com.zhulang.waveedu.common.util;
import java.text.DecimalFormat;
/**
 * 文件格式工具
 *
 * @author 狐狸半面添
 * @create 2023-02-09 19:43
 */
public class FileFormatUtils {
    /**
     * 将字节单位的文件大小转为格式化的文件大小表示
     *
     * @param fileLength 文件字节大小
     * @return 格式化文件大小表示
     */
    public static String formatFileSize(long fileLength) {
        DecimalFormat df = new DecimalFormat("#.00");
        String fileSizeString = "";
        String wrongSize = "0B";
        if (fileLength == 0) {
            return wrongSize;
        }
        if (fileLength < 1024) {
            fileSizeString = df.format((double) fileLength) + " B";
        } else if (fileLength < 1048576) {
            fileSizeString = df.format((double) fileLength / 1024) + " KB";
        } else if (fileLength < 1073741824) {
            fileSizeString = df.format((double) fileLength / 1048576) + " MB";
        } else {
            fileSizeString = df.format((double) fileLength / 1073741824) + " GB";
        }
        return fileSizeString;
    }
}

5.8 CipherUtils加密解密工具类

package com.zhulang.waveedu.common.util;
import com.alibaba.fastjson.JSON;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
/**
 * AES加密解密工具
 *
 * @author 狐狸半面添
 * @create 2023-01-18 20:34
 */
public class CipherUtils {
    private static final String SECRET_KEY = "tangyulang5201314";
    private static final String AES = "AES";
    private static final String CHARSET_NAME = "UTF-8";
    /**
     * 生成密钥 key
     *
     * @param password 加密密码
     * @return
     * @throws Exception
     */
    private static SecretKeySpec generateKey(String password) throws Exception {
        // 1.构造密钥生成器,指定为AES算法,不区分大小写
        KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
        // 2. 因为AES要求密钥的长度为128,我们需要固定的密码,因此随机源的种子需要设置为我们的密码数组
        // 生成一个128位的随机源, 根据传入的字节数组
        /*
         * 这种方式 windows 下正常, Linux 环境下会解密失败
         * keyGenerator.init(128, new SecureRandom(password.getBytes()));
         */
        // 兼容 Linux
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        random.setSeed(password.getBytes());
        keyGenerator.init(128, random);
        // 3.产生原始对称密钥
        SecretKey original_key = keyGenerator.generateKey();
        // 4. 根据字节数组生成AES密钥
        return new SecretKeySpec(original_key.getEncoded(), AES);
    }
    /**
     * 加密
     *
     * @param content  加密的内容
     * @param password 加密密码
     * @return
     */
    private static String aESEncode(String content, String password) {
        try {
            // 根据指定算法AES自成密码器
            Cipher cipher = Cipher.getInstance(AES);
            // 基于加密模式和密钥初始化Cipher
            cipher.init(Cipher.ENCRYPT_MODE, generateKey(password));
            // 单部分加密结束, 重置Cipher, 获取加密内容的字节数组(这里要设置为UTF-8)防止解密为乱码
            byte[] bytes = cipher.doFinal(content.getBytes(CHARSET_NAME));
            // 将加密后的字节数组转为字符串返回
            return Base64.getUrlEncoder().encodeToString(bytes);
        } catch (Exception e) {
            // 如果有错就返回 null
            return null;
        }
    }
    /**
     * 解密
     *
     * @param content  解密内容
     * @param password 解密密码
     * @return
     */
    private static String AESDecode(String content, String password) {
        try {
            // 将加密并编码后的内容解码成字节数组
            byte[] bytes = Base64.getUrlDecoder().decode(content);
            // 这里指定了算法为AES
            Cipher cipher = Cipher.getInstance(AES);
            // 基于解密模式和密钥初始化Cipher
            cipher.init(Cipher.DECRYPT_MODE, generateKey(password));
            // 单部分加密结束,重置Cipher
            byte[] result = cipher.doFinal(bytes);
            // 将解密后的字节数组转成 UTF-8 编码的字符串返回
            return new String(result, CHARSET_NAME);
        } catch (Exception e) {
            // 如果有错就返回 null
            return null;
        }
    }
    /**
     * 加密
     *
     * @param content 加密内容
     * @return 加密结果
     */
    public static String encrypt(String content) {
        return aESEncode(content, SECRET_KEY);
    }
    /**
     * 解密
     *
     * @param content 解密内容
     * @return 解密结果
     */
    public static String decrypt(String content) {
        try {
            return AESDecode(content, SECRET_KEY);
        } catch (Exception e) {
            return null;
        }
    }
}

5.9 MediaFile.java

package com.zhulang.waveedu.service.po;
import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
 * <p>
 * 第三方服务-媒资文件表
 * </p>
 *
 * @author 狐狸半面添
 * @since 2023-02-08
 */
@TableName("service_media_file")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MediaFile implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 主键id(雪花算法)
     */
    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private Long id;
    /**
     * 文件名称
     */
    private String fileName;
    /**
     * 文件类型:文本,图片,音频,视频,其它
     */
    private String fileType;
    /**
     * 文件格式
     */
    private String fileFormat;
    /**
     * 标签
     */
    private String tag;
    /**
     * 存储桶
     */
    private String bucket;
    /**
     * 文件存储路径
     */
    private String filePath;
    /**
     * 文件的md5值
     */
    private String fileMd5;
    /**
     * 文件字节大小
     */
    private Long fileByteSize;
    /**
     * 文件格式化大小
     */
    private String fileFormatSize;
    /**
     * 上传人id
     */
    private Long userId;
    /**
     * 创建时间(上传时间)
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    /**
     * 修改时间
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}

相关文章
|
6月前
|
JavaScript 前端开发 Java
springboot整合minio+vue实现大文件分片上传,断点续传(复制可用,包含minio工具类)
springboot整合minio+vue实现大文件分片上传,断点续传(复制可用,包含minio工具类)
1695 0
|
6月前
|
前端开发 NoSQL Redis
如何实现大文件上传:秒传、断点续传、分片上传
如何实现大文件上传:秒传、断点续传、分片上传
511 0
|
6月前
|
存储
fastdfs源码阅读:上传和下载(文件客户端逻辑)
fastdfs源码阅读:上传和下载(文件客户端逻辑)
253 0
|
6月前
|
Kubernetes Windows 容器
minio上传下载
minio上传下载
133 0
|
存储 前端开发 NoSQL
注册java实现文件分片上传并且断点续传
一、简单的分片上传 针对第一个问题,如果文件过大,上传到一半断开了,若重新开始上传的话,会很消耗时间,并且你也并不知道距离上次断开时,已经上传到哪一部分了。因此我们应该先对大文件进行分片处理,防止上面提到的问题。
|
存储 前端开发 算法
minio&前后端分离上传视频/上传大文件——前后端分离断点续传&minio分片上传实现(三)
minio&前后端分离上传视频/上传大文件——前后端分离断点续传&minio分片上传实现
615 0
|
前端开发 Java 数据库
minio&前后端分离上传视频/上传大文件——前后端分离断点续传&minio分片上传实现(一)
minio&前后端分离上传视频/上传大文件——前后端分离断点续传&minio分片上传实现
1091 0
|
存储 分布式计算 网络协议
文件上传下载系列——大文件分片上传
文件上传下载系列——大文件分片上传
|
前端开发
后端处理图片的上传和下载
后端处理图片的上传和下载
166 0
|
前端开发 关系型数据库 MySQL
大文件上传
大文件上传
166 0