Spring Boot 整合 Minio

devtools/2024/10/15 15:21:12/

一、导入依赖

<!-- MinIO 客户端 -->
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.5.7</version>
</dependency><!-- OkHttp 是一个高效的网络库 -->
<dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.9.3</version>
</dependency>

二、添加yaml配置

server:port: 3333spring:servlet:multipart:max-request-size: 200MBmax-file-size: 200MBminio:url: http://127.0.0.1:19000 #换成自己的minio服务端地址accessKey: minioadmin # 用户名secretKey: minioadmin # 密码bucketName: demo  # bucketName指的就是之前创建的MinIO桶Bucket

 三、创建配置类

import io.minio.MinioClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @ClassName MinioConfig* @Description 文件* @Date 2024/9/21 10:21*/
@Data
@Configuration
public class MinioConfig {/** 访问地址 */@Value("${minio.url}")private String endpoint;/** 唯一标识账户 */@Value("${minio.accessKey}")private String accessKey;/** 账户密码 */@Value("${minio.secretKey}")private String secretKey;/** 默认储存桶 */@Value("${minio.bucketName}")private String bucketName;/*** 标记此方法为一个 Bean,Spring 会在上下文中管理这个 Bean*/@Beanpublic MinioClient minioClient(){使用 MinioClient 的构建器模式创建一个 MinioClient 实例return MinioClient.builder()//设置 Minio 服务的端点地址.endpoint(endpoint)// 设置访问 Minio 服务所需的访问密钥和秘密密钥.credentials(accessKey, secretKey)// 构建并返回 MinioClient 实例.build();}
}

四、创建工具类

java">import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;/*** Minio工具类*/
@Log4j2
@Component
@RequiredArgsConstructor
public class MinioUtils {/*** 定义一个私有的、不可变的 MinioClient 实例变量 minioClient*/private final MinioClient minioClient;/*** 启动SpringBoot容器的时候初始化Bucket(桶)* 如果没有Bucket(桶)则创建** @param bucketName Bucket(桶)名称*/@SneakyThrows(Exception.class)private void createBucket(String bucketName) {if (!bucketExists(bucketName)) {//使用 minioClient 的 makeBucket 方法创建一个新的桶。//MakeBucketArgs.builder().bucket(bucketName).build() 创建了一个请求参数对象,指定了要创建的桶的名称。minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}}/*** 判断Bucket(桶)是否存在** @param bucketName Bucket(桶)名称* @return true:存在  false:不存在*/@SneakyThrows(Exception.class)public boolean bucketExists(String bucketName) {//调用 minioClient 对象的 bucketExists 方法,检查指定的桶是否存在。//BucketExistsArgs.builder().bucket(bucketName).build() 创建了一个请求参数对象,用于传递桶名称。return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());}/*** 获得Bucket(桶)的策略** @param bucketName Bucket(桶)名称* @return 指定桶的访问策略*/@SneakyThrows(Exception.class)public String getBucketPolicy(String bucketName) {//通过 MinIO 客户端 API 获取指定桶的访问策略。return minioClient.getBucketPolicy(//使用构建者模式创建一个 GetBucketPolicyArgs 对象,以便设置请求参数。GetBucketPolicyArgs.builder()//设置要获取策略的桶的名称。.bucket(bucketName)//构建并返回 GetBucketPolicyArgs 对象,该对象将作为参数传递给 getBucketPolicy 方法。.build());}/*** 获得所有Bucket(桶)列表** @return 所有桶的列表*/@SneakyThrows(Exception.class)public List<Bucket> getAllBuckets() {//调用 MinIO 客户端的 listBuckets 方法,返回当前用户拥有的所有桶的列表。return minioClient.listBuckets();}/*** 根据bucketName获取其相关信息** @param bucketName Bucket(桶)名称* @return Optional<Bucket>*/@SneakyThrows(Exception.class)public Optional<Bucket> getBucket(String bucketName) {//查找并返回指定名称的桶,如果不存在,则返回 Optional.empty()。//调用 getAllBuckets() 方法获取所有桶,然后使用 Java 8 的 Stream API 进行处理。//filter 方法用于筛选出名称与 bucketName 匹配的桶。//findFirst 方法用于返回第一个匹配的桶,返回值是一个 Optional<Bucket>,表示可能存在的桶。return getAllBuckets().stream().filter(bucket -> bucket.name().equals(bucketName)).findFirst();}/*** 根据bucketName删除Bucket(桶)  true:删除成功  false:删除失败** @param bucketName Bucket(桶)名称*/@SneakyThrows(Exception.class)public void removeBucket(String bucketName) {//调用 MinIO 客户端的 removeBucket 方法,通过构建 RemoveBucketArgs 对象来指定要删除的桶名。minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());}/*** 判断文件是否存在** @param bucketName Bucket(桶)名称* @param objectName 文件名称* @return true:存在  false:不存在*/public boolean isObjectExist(String bucketName, String objectName) {boolean exist = true;try {//调用 MinIO 客户端的 statObject 方法,构建 StatObjectArgs 对象以检查对象的状态。如果对象存在,该方法不会抛出异常。minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());} catch (Exception e) {log.error("【Minio工具类】>>>> 判断文件是否存在, 异常:", e);exist = false;}return exist;}/*** 判断文件夹是否存在** @param bucketName Bucket(桶)名称* @param objectName 文件夹名称* @return true:存在  false:不存在*/public boolean isFolderExit(String bucketName, String objectName) {boolean exist = true;try {//使用 listObjects 方法列出指定桶中以 objectName 为前缀的对象。recursive(false) 表示不进行递归查找。Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).recursive(false).build());//遍历结果,检查每个 Item 是否为目录且名称与 objectName 匹配。如果找到匹配的目录,将 exist 设为 true。for (Result<Item> result : results) {Item item = result.get();if (item.isDir() && objectName.equals(item.objectName())) {exist = true;}}} catch (Exception e) {log.error("【Minio工具类】>>>> 判断文件夹是否存在, 异常:", e);exist = false;}return exist;}/*** 根据文件前置查询文件** @param bucketName 存储桶* @param prefix     前缀* @param recursive  是否使用递归查询* @return MinioItem 列表*/@SneakyThrows(Exception.class)public List<Item> getAllObjectsByPrefix(String bucketName,String prefix,boolean recursive) {List<Item> list = new ArrayList<>();// 创建一个空的 Item 列表,用于存储结果Iterable<Result<Item>> objectsIterator = minioClient.listObjects(// 获取指定桶中以 prefix 开头的对象列表ListObjectsArgs.builder()// 构建 ListObjectsArgs 对象.bucket(bucketName)// 设置桶名.prefix(prefix)// 设置对象前缀.recursive(recursive)// 设置是否递归查找.build());// 构建参数if (objectsIterator != null) { // 检查对象迭代器是否不为 nullfor (Result<Item> o : objectsIterator) { // 遍历迭代器中的每个结果Item item = o.get();// 获取当前结果中的 Item 对象list.add(item);// 将 Item 添加到列表中}}return list;// 返回包含所有找到的 Item 的列表}/*** 获取文件流** @param bucketName 存储桶* @param objectName 文件名* @return 二进制流*/@SneakyThrows(Exception.class)public InputStream getObject(String bucketName, String objectName) {return minioClient.getObject(// 调用 MinIO 客户端的 getObject 方法,获取指定对象的输入流GetObjectArgs.builder()// 构建 GetObjectArgs 对象.bucket(bucketName)// 设置桶名.object(objectName)// 设置对象名.build()); // 构建参数并调用 getObject}/*** 断点下载** @param bucketName 存储桶* @param objectName 文件名称* @param offset     起始字节的位置* @param length     要读取的长度* @return 二进制流*/@SneakyThrows(Exception.class)public InputStream getObject(String bucketName, String objectName, long offset, long length) {return minioClient.getObject(// 调用 MinIO 客户端的 getObject 方法,获取指定对象的输入流GetObjectArgs.builder()// 构建 GetObjectArgs 对象.bucket(bucketName) // 设置桶名.object(objectName)// 设置对象名.offset(offset)// 设置读取的起始偏移量.length(length) // 设置要读取的字节长度.build());// 构建参数并调用 getObject}/*** 获取路径下文件列表** @param bucketName 存储桶* @param prefix     文件名称* @param recursive  是否递归查找,false:模拟文件夹结构查找* @return 二进制流*/public Iterable<Result<Item>> listObjects(String bucketName, String prefix, boolean recursive) {return minioClient.listObjects(// 调用 MinIO 客户端的 listObjects 方法,返回对象列表ListObjectsArgs.builder() // 构建 ListObjectsArgs 对象.bucket(bucketName) // 设置桶名.prefix(prefix)// 设置对象前缀.recursive(recursive)// 设置是否递归查找.build());// 构建参数并调用 listObjects}/*** 使用MultipartFile进行文件上传** @param bucketName  存储桶* @param file        文件名* @param objectName  对象名* @param contentType 类型* @return*/@SneakyThrows(Exception.class)public ObjectWriteResponse uploadFile(String bucketName, MultipartFile file, String objectName, String contentType) {InputStream inputStream = file.getInputStream(); // 获取文件的输入流,以便上传return minioClient.putObject(// 调用 MinIO 客户端的 putObject 方法,上传文件PutObjectArgs.builder()// 构建 PutObjectArgs 对象.bucket(bucketName)// 设置桶名.object(objectName)// 设置对象名.contentType(contentType)// 设置文件的内容类型.stream(inputStream, inputStream.available(), -1) // 设置输入流及其可用字节数,-1 表示不限制.build());// 构建参数并调用 putObject}/*** 图片上传** @param bucketName  存储桶* @param imageBase64 图像的 Base64 编码字符串* @param imageName   接收图像的原始名称* @return*/public ObjectWriteResponse uploadImage(String bucketName, String imageBase64, String imageName) {if (!StringUtils.isEmpty(imageBase64)) {// 检查 Base64 字符串是否非空InputStream in = base64ToInputStream(imageBase64);// 将 Base64 字符串转换为输入流String newName = System.currentTimeMillis() + "_" + imageName + ".jpg";// 生成新文件名,包含当前时间戳和原始名称String year = String.valueOf(new Date().getYear());// 获取当前年份String month = String.valueOf(new Date().getMonth());// 获取当前月份return uploadFile(bucketName, year + "/" + month + "/" + newName, in); // 调用 uploadFile 方法上传文件,路径为 年/月/新文件名}return null;// 如果 Base64 字符串为空,返回 null}// BASE64Decoder在jdk8以上的版本移除了,报错最简单解决换成jdk8就行了public static InputStream base64ToInputStream(String base64) {ByteArrayInputStream stream = null;// 声明 ByteArrayInputStream 变量,用于保存字节流try {// 使用 BASE64Decoder 解码 Base64 字符串并去除首尾空格,得到字节数组byte[] bytes = new BASE64Decoder().decodeBuffer(base64.trim());// 将字节数组转换为 ByteArrayInputStream,以便返回stream = new ByteArrayInputStream(bytes);} catch (Exception e) {e.printStackTrace();}return stream;// 返回字节输入流,如果出错则返回 null}/*** 上传本地文件** @param bucketName 存储桶* @param objectName 对象名称* @param fileName   本地文件路径* @return*/@SneakyThrows(Exception.class)public ObjectWriteResponse uploadFile(String bucketName, String objectName, String fileName) {return minioClient.uploadObject(// 调用 MinIO 客户端的 uploadObject 方法进行文件上传UploadObjectArgs.builder()// 使用 UploadObjectArgs 构建上传参数.bucket(bucketName)// 设置存储桶名称.object(objectName)// 设置对象名称(在存储桶中的文件名).filename(fileName)// 设置要上传的本地文件名.build()); // 构建并返回 UploadObjectArgs 对象,然后执行上传}/*** 通过流上传文件** @param bucketName  存储桶* @param objectName  文件对象* @param inputStream 文件流* @return*/@SneakyThrows(Exception.class)public ObjectWriteResponse uploadFile(String bucketName, String objectName, InputStream inputStream) {return minioClient.putObject( // 调用 MinIO 客户端的 putObject 方法进行文件上传PutObjectArgs.builder()// 使用 PutObjectArgs 构建上传参数.bucket(bucketName)// 设置存储桶名称.object(objectName)// 设置对象名称(在存储桶中的文件名).stream(inputStream, inputStream.available(), -1)// 设置输入流及其大小,-1 表示使用默认值.build()); // 构建并返回 PutObjectArgs 对象,然后执行上传}/*** 创建文件夹或目录** @param bucketName 存储桶* @param objectName 目录路径* @return*/@SneakyThrows(Exception.class)public ObjectWriteResponse createDir(String bucketName, String objectName) {return minioClient.putObject(// 调用 MinIO 客户端的 putObject 方法进行上传PutObjectArgs.builder()// 使用 PutObjectArgs 构建上传参数.bucket(bucketName)// 设置存储桶名称.object(objectName) // 设置对象名称(在存储桶中的“目录”名称).stream(new ByteArrayInputStream(new byte[]{}), 0, -1)// 使用空的字节数组流,表示创建一个空对象,0 表示长度,-1 表示使用默认值.build());// 构建并返回 PutObjectArgs 对象,然后执行上传}/*** 获取文件信息, 如果抛出异常则说明文件不存在** @param bucketName 存储桶* @param objectName 文件名称* @return*/@SneakyThrows(Exception.class)public String getFileStatusInfo(String bucketName, String objectName) {return minioClient.statObject( // 调用 MinIO 客户端的 statObject 方法获取对象状态信息StatObjectArgs.builder() // 使用 StatObjectArgs 构建请求参数.bucket(bucketName) // 设置存储桶名称.object(objectName)// 设置对象名称.build() // 构建并返回 StatObjectArgs 对象).toString();// 调用 toString 方法,将获取的状态信息转换为字符串并返回}/*** 拷贝文件** @param bucketName    存储桶* @param objectName    文件名* @param srcBucketName 目标存储桶* @param srcObjectName 目标文件名*/@SneakyThrows(Exception.class)public ObjectWriteResponse copyFile(String bucketName, String objectName, String srcBucketName, String srcObjectName) {return minioClient.copyObject(// 调用 MinIO 客户端的 copyObject 方法进行对象复制CopyObjectArgs.builder()// 使用 CopyObjectArgs 构建复制参数.source(CopySource.builder()// 设置复制源.bucket(bucketName) // 源存储桶名称.object(objectName)// 源对象名称.build())// 构建并返回 CopySource 对象.bucket(srcBucketName)// 设置目标存储桶名称.object(srcObjectName) // 设置目标对象名称.build() // 构建并返回 CopyObjectArgs 对象);// 执行对象复制并返回 ObjectWriteResponse}/*** 删除文件** @param bucketName 存储桶* @param objectName 文件名称*/@SneakyThrows(Exception.class)public void removeFile(String bucketName, String objectName) {minioClient.removeObject(// 调用 MinIO 客户端的 removeObject 方法以删除指定对象RemoveObjectArgs.builder()// 使用 RemoveObjectArgs 构建删除参数.bucket(bucketName) // 设置要删除对象的存储桶名称.object(objectName)// 设置要删除的对象名称.build());// 构建并返回 RemoveObjectArgs 对象,并执行删除操作}/*** 批量删除文件** @param bucketName 存储桶* @param keys       需要删除的文件列表* @return*/public void removeFiles(String bucketName, List<String> keys) {List<DeleteObject> objects = new LinkedList<>();// 创建一个 LinkedList 用于存储待删除对象keys.forEach(s -> {// 遍历对象键列表objects.add(new DeleteObject(s)); // 将每个键封装成 DeleteObject 并添加到列表中try {// 尝试执行删除操作removeFile(bucketName, s);// 调用 removeFile 方法删除指定对象} catch (Exception e) {log.error("【Minio工具类】>>>> 批量删除文件,异常:", e);}});}/*** 获取文件外链* 使用了 .expiry(expires) 方法,指定了预签名 URL 的过期时间。* 适用于需要限制 URL 有效期的场景。* 适用于需要设定过期时间的情况** @param bucketName 存储桶* @param objectName 文件名* @param expires    过期时间 <=7 秒 (外链有效时间(单位:秒))* @return url*/@SneakyThrows(Exception.class)public String getPresignedObjectUrl(String bucketName, String objectName, Integer expires) {// 构建获取预签名对象 URL 的参数GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()// 使用 GetPresignedObjectUrlArgs 的构建器.expiry(expires)// 设置 URL 的过期时间(以秒为单位).bucket(bucketName) // 设置存储桶名称.object(objectName)// 设置对象名称.build();// 构建并返回 GetPresignedObjectUrlArgs 对象return minioClient.getPresignedObjectUrl(args);// 调用 MinIO 客户端获取预签名 URL 并返回}/*** 获得文件外链* 使用了 .method(Method.GET) 方法,指定了 HTTP 请求方法为 GET。* 适用于需要明确请求类型的场景,但没有设置过期时间。* 适用于无需设定过期时间的访问** @param bucketName* @param objectName* @return url*/@SneakyThrows(Exception.class)public String getPresignedObjectUrl(String bucketName, String objectName) {// 构建获取预签名对象 URL 的参数GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()// 使用 GetPresignedObjectUrlArgs 的构建器.bucket(bucketName) // 设置存储桶名称.object(objectName)// 设置对象名称.method(Method.GET)// 设置对象名称.build();// 构建并返回 GetPresignedObjectUrlArgs 对象return minioClient.getPresignedObjectUrl(args);// 调用 MinIO 客户端获取预签名 URL 并返回}/*** 将URLDecoder编码转成UTF8** @param str* @return* @throws UnsupportedEncodingException*/public String getUtf8ByURLDecoder(String str) throws UnsupportedEncodingException {// 将字符串中的不合法 URL 编码转换为合法编码String url = str.replaceAll("%(?![0-9a-fA-F]{2})", "%25");// 使用正则表达式,将不符合 URL 编码规范的百分号(%)替换为 %25return URLDecoder.decode(url, "UTF-8");// 使用 URLDecoder 解码字符串,返回 UTF-8 编码的结果}}

一些解释

1.@Log4j2:这是一个 Lombok 提供的注解,用于自动生成一个 Log4j2 日志记录器。使用此注解后,你可以直接调用 log 对象来记录日志,而无需手动创建日志实例。

2.@Component:这是 Spring 框架的注解,表示该类是一个 Spring 组件,Spring 会自动检测并将其注册为一个 Bean。可以用于自动装配和依赖注入。

3.@RequiredArgsConstructor:也是 Lombok 提供的注解,自动生成一个构造函数,该构造函数接受所有被 final 修饰的字段或带有 @NonNull 注解的字段。这简化了依赖注入的过程,使代码更加简洁。

4.@SneakyThrows(Exception.class) :是 Lombok 提供的一个注解,用于在方法上处理检查性异常。

作用:

自动处理异常:在使用 @SneakyThrows 注解的方法中,如果抛出任何类型的检查性异常(即编译器要求显式捕获或声明的异常),Lombok 会自动将其包装成运行时异常(RuntimeException),从而避免了需要显式捕获或声明这些异常的麻烦。

注意事项:

尽管 @SneakyThrows 使代码更简洁,但在调试或异常处理时,要小心隐式处理异常可能导致的问题。如果可能,考虑在应用程序中适当处理异常,以提高代码的可读性和可维护性。

5‌‌.Optional‌是Java 8引入的一个新的容器对象,它提供了非常丰富的API,主要是为了解决空指针异常的问题。Optional类允许你创建一个可能为null的值的容器,从而避免了直接使用null值可能导致的空指针异常。它提供了多种方法来操作这个容器,包括判断值是否存在(isPresent())、获取值(get())、转换值(map())、过滤值(filter())等。

6.OkHttp 是一个高效的 HTTP 客户端库,用于发送和接收网络请求,它支持同步和异步调用,自动处理常见的网络问题如重定向、HTTPS握手和连接池。它广泛用于 Android 和 Java 应用程序中,以提高网络通信的效率和稳定性。

五、创建controller层

java">/*** Minio控制层*/
@Log4j2
@RestController
@RequestMapping("/minio")
public class MinioController {@Autowiredprivate MinioUtils minioUtils;@Autowiredprivate MinioConfig minioConfig;/*** 文件上传** @param file*/@PostMapping("/upload")public String upload(@RequestParam("file") MultipartFile file) {try {//文件名String fileName = file.getOriginalFilename();String newFileName = System.currentTimeMillis() + "." + StringUtils.substringAfterLast(fileName, ".");//类型String contentType = file.getContentType();minioUtils.uploadFile(minioConfig.getBucketName(), file, newFileName, contentType);return "上传成功";} catch (Exception e) {log.error("上传失败",e);return "上传失败";}}/*** 删除** @param fileName*/@DeleteMapping("/")public void delete(@RequestParam("fileName") String fileName) {minioUtils.removeFile(minioConfig.getBucketName(), fileName);}/*** 获取文件信息** @param fileName* @return*/@GetMapping("/info")public String getFileStatusInfo(@RequestParam("fileName") String fileName) {return minioUtils.getFileStatusInfo(minioConfig.getBucketName(), fileName);}/*** 获取文件外链** @param fileName* @return*/@GetMapping("/url")public String getPresignedObjectUrl(@RequestParam("fileName") String fileName) {return minioUtils.getPresignedObjectUrl(minioConfig.getBucketName(), fileName);}/*** 文件下载** @param fileName* @param response*/@GetMapping("/download")public void download(@RequestParam("fileName") String fileName, HttpServletResponse response) {try {// 从 MinIO 获取指定的对象(文件)的输入流InputStream fileInputStream = minioUtils.getObject(minioConfig.getBucketName(), fileName);// 设置响应头,指明这是一个附件下载,并指定下载文件的名称response.setHeader("Content-Disposition", "attachment;filename=" + fileName);// 设置响应的内容类型为强制下载response.setContentType("application/force-download");// 设置响应的字符编码为 UTF-8response.setCharacterEncoding("UTF-8");// 将输入流中的文件内容复制到响应输出流中IOUtils.copy(fileInputStream, response.getOutputStream());response.flushBuffer(); // 刷新响应输出流} catch (Exception e) {log.error("下载失败");System.out.println(e);}}


http://www.ppmy.cn/devtools/126218.html

相关文章

uni-app之旅-day06-加入购物车

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言8.0 创建 cart 分支8.1 配置 vuex8.2 创建购物车的 store 模块8.3 在商品详情页中使用 Store 中的数据8.4 实现加入购物车的功能8.5 动态统计购物车中商品的总数…

Vulnhub靶场案例渗透[6]- DC6

文章目录 1. 靶场搭建2. 信息收集2.1 确定靶机ip2.2 主机信息收集2.3 主机目录扫描2.4 网站用户名和密码爆破 3. 反弹shell4. 提权 1. 靶场搭建 靶场源地址 检验下载文件的检验码&#xff0c;对比没问题使用vmware打开 # windwos 命令 Get-FileHash <filePath> -Algori…

Spring Boot的实用内置功能详解

Spring Boot作为一款备受欢迎的Java框架&#xff0c;以其简洁、高效和易用的特点&#xff0c;赢得了广大开发者的青睐。其内置的多种功能更是为开发者提供了极大的便利&#xff0c;本文将详细介绍Spring Boot中记录请求数据、请求/响应包装器、特殊的过滤器Filter以及Controlle…

FFmpeg的简单使用【Windows】--- 视频混剪+添加背景音乐

一、功能描述 点击背景音乐区域的【选择文件】按钮&#xff0c;选择音频文件并将其上传到服务器&#xff0c;上传成功后会将其存储的位置路径返回。 然后&#xff0c;点击要处理视频区域的【选择文件】按钮选择要进行混剪的视频素材&#xff08;1-10个&#xff09;。 以上两…

C语言语法练习20题(变量、输入输出、表达式与顺序语句)

题目1&#xff1a;A B 题目链接 输入两个整数&#xff0c;求这两个整数的和是多少。 输入格式 输入两个整数A, B&#xff0c;用空格隔开。 输出格式 输出一个整数&#xff0c;表示这两个数的和。 数据范围 0 ≤ A, B ≤ 10^8 输入样例 3 4输出样例 7解答&#xff0…

深入理解Transformer的笔记记录(精简版本)NNLM → Word2Vec

文章的整体介绍顺序为: NNLM → Word2Vec → Seq2Seq → Seq2Seq with Attention → Transformer → Elmo → GPT → BERT 自然语言处理相关任务中要将自然语言交给机器学习中的算法来处理,通常需要将语言数学化,因为计算机机器只认数学符号。向量是人把自然界的东西抽象出…

【华为HCIP实战课程十二】OSPF网络中1类2类LSA SPF详解,网络工程师

一、OSPF 1类LSA详解 1、通告者(产生LSA的设备):任何一台设备都会产生1类LSA 2、通告的范围:区域内部 3、功能和内容:产生拓扑信息和路由信息 LSA是OSPF链路状态信息的载体 4、每台OSPF路由器使用一条Router-LSA描述本区域内的链路状态信息 Type :LSA类型,Router-L…

C语言笔记 13

初见函数 求素数的和 #include <stdio.h>int main() {int m,n;int sum 0;int cnt 0;int i;scanf("%d %d", &m, &n);// m10,n31;if( m1) m2;for( im; i<n; i ) {int isPrime 1;int k;for ( k2; k<i-1; k ) {if ( i%k 0 ) {isPrime 0;break;…