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; }