分析若依的文件上传处理逻辑
分析若依的文件上传处理逻辑

注:已经从若依框架完成拆分,此处单独分析一下人家精彩的封装,也来理解一下怎么做一个通用的上传接口!如有分析的,理解的不透彻的地方,大家多多包含,欢迎批评指正,但是请不要恶语相向!
控制层代码剖析
@PostMapping("/upload")public AjaxResult uploadFile(MultipartFile file){try {// 上传文件路径String filePath = RuoYiConfig.getUploadPath();// 上传并返回新文件名称String fileName = FileUploadUtils.upload(filePath, file);String url = serverConfig.getUrl() + fileName;AjaxResult ajax = AjaxResult.success();ajax.put("url", url);ajax.put("fileName", fileName);ajax.put("newFileName", FileUtils.getName(fileName));ajax.put("originalFilename", file.getOriginalFilename());return ajax;} catch (Exception e) {return AjaxResult.error(e.getMessage());}}
-
@PostMapping("/upload"):这是一个用于处理HTTP POST请求的注解,它将请求映射到/upload路径。在这里,它用于处理文件上传请求。 -
public AjaxResult uploadFile(MultipartFile file):这是处理文件上传的方法。它接收一个MultipartFile对象,这是Spring提供的用于处理文件上传的类。 -
String filePath = RuoYiConfig.getUploadPath();:获取文件上传路径,通过RuoYiConfig.getUploadPath()方法获取,是从配置文件中读取的上传路径。server:port: 8888 # 项目相关配置 ruoyi:# 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)profile: ./ -
String fileName = FileUploadUtils.upload(filePath, file);:调用FileUploadUtils.upload方法实现文件上传,该方法包含文件存储逻辑,根据传入的文件路径和MultipartFile对象,返回新的文件名。下一标题我们将会着重对于这个方法进行解析。 -
String url = serverConfig.getUrl() + fileName;:构造文件的访问URL,是通过拼接服务器的URL和上传后的文件名得到的。 -
AjaxResult ajax = AjaxResult.success();:创建一个成功的AjaxResult对象,用于封装返回给客户端的数据。 -
ajax.put("url", url);:将文件的访问URL放入AjaxResult中,以便客户端获取上传后的文件的访问地址。 -
ajax.put("fileName", fileName);:将上传后的文件名放入AjaxResult中。 -
ajax.put("newFileName", FileUtils.getName(fileName));:将上传后的文件名去除路径的部分,只保留文件名放入AjaxResult中。 -
ajax.put("originalFilename", file.getOriginalFilename());:将原始文件名放入AjaxResult中。 -
return ajax;:返回封装了文件相关信息的AjaxResult对象,向客户端提供文件上传成功的响应。 -
} catch (Exception e) { return AjaxResult.error(e.getMessage());}:捕获可能的异常,如果发生异常,返回一个包含异常信息的错误AjaxResult对象,向客户端提供文件上传失败的响应。
upload文件上传方法
点击方法跳进去之后我们可以看到如下代码
package com.it_wanghui_cn.file.utils;import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Objects;import com.it_wanghui_cn.file.config.RuoYiConfig;
import com.it_wanghui_cn.file.constant.Constants;
import com.it_wanghui_cn.file.exception.FileNameLengthLimitExceededException;
import com.it_wanghui_cn.file.exception.FileSizeLimitExceededException;
import com.it_wanghui_cn.file.exception.InvalidExtensionException;
import com.it_wanghui_cn.file.utils.uuid.Seq;
import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;/*** 文件上传工具类** @author ruoyi*/
public class FileUploadUtils
{/*** 默认大小 50M*/public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;/*** 默认大小 50M*/public static final long DEFAULT_APP_MAX_SIZE = 200 * 1024 * 1024;/*** 默认的文件名最大长度 100*/public static final int DEFAULT_FILE_NAME_LENGTH = 100;/*** 默认上传的地址*/private static String defaultBaseDir = RuoYiConfig.getProfile();public static void setDefaultBaseDir(String defaultBaseDir){FileUploadUtils.defaultBaseDir = defaultBaseDir;}public static String getDefaultBaseDir(){return defaultBaseDir;}/*** 以默认配置进行文件上传** @param file 上传的文件* @return 文件名称* @throws Exception*/public static final String upload(MultipartFile file) throws IOException{try{return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);}catch (Exception e){throw new IOException(e.getMessage(), e);}}/*** 根据文件路径上传** @param baseDir 相对应用的基目录* @param file 上传的文件* @return 文件名称* @throws IOException*/public static final String upload(String baseDir, MultipartFile file) throws IOException{try{return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);}catch (Exception e){throw new IOException(e.getMessage(), e);}}/*** 文件上传** @param baseDir 相对应用的基目录* @param file 上传的文件* @param allowedExtension 上传文件类型* @return 返回上传成功的文件名* @throws FileSizeLimitExceededException 如果超出最大大小* @throws FileNameLengthLimitExceededException 文件名太长* @throws IOException 比如读写文件出错时* @throws InvalidExtensionException 文件校验异常*/public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,InvalidExtensionException{int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH){throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);}assertAllowed(file, allowedExtension);String fileName = extractFilename(file);String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();file.transferTo(Paths.get(absPath));return getPathFileName(baseDir, fileName);}/*** 编码文件名*/public static final String extractFilename(MultipartFile file){return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));}public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException{File desc = new File(uploadDir + File.separator + fileName);if (!desc.exists()){if (!desc.getParentFile().exists()){desc.getParentFile().mkdirs();}}return desc;}public static final String getPathFileName(String uploadDir, String fileName) throws IOException{int dirLastIndex = RuoYiConfig.getProfile().length() + 1;String currentDir = StringUtils.substring(uploadDir, dirLastIndex);return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;}/*** 文件大小校验** @param file 上传的文件* @return* @throws FileSizeLimitExceededException 如果超出最大大小* @throws InvalidExtensionException*/public static final void assertAllowed(MultipartFile file, String[] allowedExtension)throws FileSizeLimitExceededException, InvalidExtensionException{long size = file.getSize();String fileSuffix;if (null != file.getOriginalFilename() && file.getOriginalFilename().contains(".")) {fileSuffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")+1);}else {fileSuffix = null;}if ("apk".equals(fileSuffix) || "ipa".equals(fileSuffix)) {if (size > DEFAULT_APP_MAX_SIZE){throw new FileSizeLimitExceededException(DEFAULT_APP_MAX_SIZE / 1024 / 1024);}}else {if (size > DEFAULT_MAX_SIZE){throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);}}String fileName = file.getOriginalFilename();String extension = getExtension(file);if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)){if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION){throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,fileName);}else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION){throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,fileName);}else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION){throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,fileName);}else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION){throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,fileName);}else{throw new InvalidExtensionException(allowedExtension, extension, fileName);}}}/*** 判断MIME类型是否是允许的MIME类型** @param extension* @param allowedExtension* @return*/public static final boolean isAllowedExtension(String extension, String[] allowedExtension){for (String str : allowedExtension){if (str.equalsIgnoreCase(extension)){return true;}}return false;}/*** 获取文件名的后缀** @param file 表单文件* @return 后缀名*/public static final String getExtension(MultipartFile file){String extension = FilenameUtils.getExtension(file.getOriginalFilename());if (StringUtils.isEmpty(extension)){extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));}return extension;}
}
这是一个文件上传工具类,主要用于处理文件上传的相关逻辑。
-
常量定义:
DEFAULT_MAX_SIZE:默认文件大小限制为50MB。DEFAULT_APP_MAX_SIZE:默认App文件大小限制为200MB。DEFAULT_FILE_NAME_LENGTH:默认文件名最大长度为100字符。defaultBaseDir:默认的文件上传基目录,初始化时可能从RuoYiConfig中获取。
-
上传文件方法:
-
upload(MultipartFile file):使用默认配置上传文件,调用upload(String baseDir, MultipartFile file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION)。 -
upload(String baseDir, MultipartFile file):根据给定的基目录上传文件,调用upload(String baseDir, MultipartFile file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION)。 -
upload(String baseDir, MultipartFile file, String[] allowedExtension):文件上传的核心方法,包含文件大小、文件名长度和文件扩展名的校验逻辑。public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,InvalidExtensionException{int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH){throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);}assertAllowed(file, allowedExtension);String fileName = extractFilename(file);String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();file.transferTo(Paths.get(absPath));return getPathFileName(baseDir, fileName);}由于这段代码是文件上传的核心方法,我们对其进行细分析:
-
文件名长度检查:
int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length(); if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); }- 获取上传文件的原始文件名,并检查其长度是否超过了设定的最大文件名长度(
DEFAULT_FILE_NAME_LENGTH)。 - 如果超过了限制,抛出
FileNameLengthLimitExceededException异常。
- 获取上传文件的原始文件名,并检查其长度是否超过了设定的最大文件名长度(
-
文件扩展名和大小校验:
assertAllowed(file, allowedExtension);-
调用
assertAllowed方法,对文件的扩展名和大小进行校验。 -
如果不符合要求,会抛出
FileSizeLimitExceededException、InvalidExtensionException等异常。public static final void assertAllowed(MultipartFile file, String[] allowedExtension)throws FileSizeLimitExceededException, InvalidExtensionException{long size = file.getSize();String fileSuffix;if (null != file.getOriginalFilename() && file.getOriginalFilename().contains(".")) {fileSuffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")+1);}else {fileSuffix = null;}if ("apk".equals(fileSuffix) || "ipa".equals(fileSuffix)) {if (size > DEFAULT_APP_MAX_SIZE){throw new FileSizeLimitExceededException(DEFAULT_APP_MAX_SIZE / 1024 / 1024);}}else {if (size > DEFAULT_MAX_SIZE){throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);}}String fileName = file.getOriginalFilename();String extension = getExtension(file);if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)){if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION){throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,fileName);}else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION){throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,fileName);}else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION){throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,fileName);}else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION){throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,fileName);}else{throw new InvalidExtensionException(allowedExtension, extension, fileName);}}}这是文件上传工具类中的文件校验方法
assertAllowed,以下是对其进行细分析:-
获取文件大小和扩展名:
long size = file.getSize(); String fileSuffix; if (null != file.getOriginalFilename() && file.getOriginalFilename().contains(".")) {fileSuffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")+1); } else {fileSuffix = null; }- 获取上传文件的大小和原始文件名中的扩展名。
-
文件大小校验:
if ("apk".equals(fileSuffix) || "ipa".equals(fileSuffix)) {if (size > DEFAULT_APP_MAX_SIZE) {throw new FileSizeLimitExceededException(DEFAULT_APP_MAX_SIZE / 1024 / 1024);} } else {if (size > DEFAULT_MAX_SIZE) {throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);} }- 根据文件扩展名(
fileSuffix)判断文件类型,如果是apk或ipa文件,检查文件大小是否超过默认限制(DEFAULT_APP_MAX_SIZE),否则检查是否超过常规文件大小限制(DEFAULT_MAX_SIZE)。 - 如果超过了大小限制,抛出
FileSizeLimitExceededException异常。
- 根据文件扩展名(
-
文件扩展名校验:
String fileName = file.getOriginalFilename(); String extension = getExtension(file); if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {// ... }- 获取上传文件的原始文件名和文件扩展名。
- 如果
allowedExtension不为空且文件扩展名不在允许的扩展名列表中,进入后续的异常判断逻辑。
-
根据文件类型抛出不同的异常:
if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) {throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, fileName); } else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) {throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, fileName); } else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) {throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, fileName); } else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) {throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, fileName); } else {throw new InvalidExtensionException(allowedExtension, extension, fileName); }- 根据不同的文件类型,抛出对应的异常。例如,如果文件类型是图片,抛出
InvalidImageExtensionException异常。
- 根据不同的文件类型,抛出对应的异常。例如,如果文件类型是图片,抛出
所以,综上所述呢,
assertAllowed方法主要用于对文件的大小和扩展名进行校验,确保文件满足预定义的条件,否则抛出相应的异常。 -
-
-
生成文件名和绝对路径:
String fileName = extractFilename(file); String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();- 调用
extractFilename方法,根据上传文件生成编码后的文件名。 - 调用
getAbsoluteFile方法,获取文件的绝对路径。
- 调用
-
文件写入磁盘:
file.transferTo(Paths.get(absPath));- 使用
transferTo方法将文件写入磁盘,具体路径由absPath决定。
- 使用
-
返回文件相对路径:
return getPathFileName(baseDir, fileName);- 调用
getPathFileName方法,生成相对于上传基目录的文件路径。
- 调用
所以,综上所述呢,这段代码通过一系列步骤完成了文件上传的核心逻辑,包括文件名长度、扩展名、大小的校验,生成文件名,将文件写入磁盘,并返回相对路径。异常的处理确保了在上传过程中出现问题时能够向上层抛出相应的异常。
-
-
-
文件名处理:
extractFilename(MultipartFile file):根据上传文件生成编码后的文件名,包括日期路径、文件基名、Seq以及文件扩展名。
public static final String extractFilename(MultipartFile file) {return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file)); }-
构造文件名:
StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));- 使用字符串格式化工具(可能是自定义的
StringUtils.format方法)构建文件名。这个文件名包括以下几个部分:{}/:日期路径,可能是根据上传日期生成的目录结构。{}_:原始文件名去除扩展名后的部分。{}:通过某种方式获取的上传序列号或ID。.:文件名的分隔符。getExtension(file):获取文件的扩展名。
- 使用字符串格式化工具(可能是自定义的
-
返回构造的文件名:
return StringUtils.format(...);- 返回构造好的文件名。
所以,综上所述呢,
extractFilename方法主要用于根据上传文件的信息构建一个新的文件名,其中包括日期路径、原始文件名的基本部分、上传序列号或ID,以及文件扩展名。这个文件名通常用于确定上传文件在服务器上的存储位置。 -
文件路径操作:
-
getAbsoluteFile(String uploadDir, String fileName):获取文件的绝对路径,并确保其父目录存在。public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException{File desc = new File(uploadDir + File.separator + fileName);if (!desc.exists()){if (!desc.getParentFile().exists()){desc.getParentFile().mkdirs();}}return desc;}这就是文件上传工具类中的
getAbsoluteFile方法,以下是对其进行细分析:-
构造文件对象:
File desc = new File(uploadDir + File.separator + fileName);- 创建一个
File对象,表示上传目录 (uploadDir) 下的特定文件 (fileName)。
- 创建一个
-
检查文件是否存在:
if (!desc.exists()) {// ... }- 判断文件是否已经存在。如果文件不存在,执行后续的逻辑。
-
创建文件父目录:
if (!desc.getParentFile().exists()) {desc.getParentFile().mkdirs(); }- 如果文件的父目录不存在,调用
mkdirs()方法创建父目录。这样可以确保文件存储路径的所有父目录都存在。
- 如果文件的父目录不存在,调用
-
返回文件对象:
return desc;- 返回表示上传文件的
File对象。
- 返回表示上传文件的
getAbsoluteFile方法主要用于获取表示上传文件的File对象,并确保文件所在的目录结构是存在的。如果文件所在的目录不存在,会先创建这些目录。这样可以保证文件写入磁盘时,其所在的目录结构是正确的。 -
-
getPathFileName(String uploadDir, String fileName):根据上传目录和文件名构造相对路径,通常用于构造访问URL。public static final String getPathFileName(String uploadDir, String fileName) throws IOException{int dirLastIndex = RuoYiConfig.getProfile().length() + 1;String currentDir = StringUtils.substring(uploadDir, dirLastIndex);return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;}
-
-
文件大小和扩展名校验:
assertAllowed(MultipartFile file, String[] allowedExtension):对上传的文件进行大小和扩展名的校验,根据文件扩展名调用isAllowedExtension方法判断是否允许上传。isAllowedExtension(String extension, String[] allowedExtension):判断文件扩展名是否在允许的扩展名列表中。
-
其他方法:
getExtension(MultipartFile file):获取文件的扩展名,优先使用原始文件名的扩展名,如果为空,则使用文件的MIME类型来获取扩展名。
总体而言,该工具类提供了一系列方法,用于方便地进行文件上传,并包含了一些常见的文件校验逻辑。
transferTo方法再深入
default void transferTo(Path dest) throws IOException, IllegalStateException {FileCopyUtils.copy(this.getInputStream(), Files.newOutputStream(dest));}
Files.newOutputStream(dest) 是 Java NIO(New I/O)库提供的一种方式,用于创建一个输出流(OutputStream)以写入文件。这方法返回一个输出流,你可以使用它来写入字节到指定的文件。
具体来说,Files.newOutputStream(dest) 的参数 dest 是一个 Path 对象,表示文件路径。下面是一些关键的点:
-
创建输出流:
Path dest = ...; // 指定文件路径 OutputStream outputStream = Files.newOutputStream(dest);- 使用
Files.newOutputStream(dest)创建一个输出流。
- 使用
-
写入数据:
byte[] data = ...; // 准备写入的数据 outputStream.write(data);- 利用得到的输出流,可以使用
write方法将数据写入文件。
- 利用得到的输出流,可以使用
-
关闭流:
outputStream.close();- 在数据写入完成后,确保调用
close方法关闭输出流,以释放相关资源。
- 在数据写入完成后,确保调用
这个方法的主要优点是简洁且易用,不需要手动创建文件或处理一些底层的操作。它是 Java NIO 中用于文件写入的一部分,提供了更灵活和高性能的 I/O 操作。
看来还是千呼万唤始出来,犹抱琵琶半遮面,我们再进一层
public static int copy(InputStream in, OutputStream out) throws IOException {Assert.notNull(in, "No InputStream specified");Assert.notNull(out, "No OutputStream specified");int var2;try {var2 = StreamUtils.copy(in, out);} finally {close(in);close(out);}return var2;}
这是一个用于将输入流(InputStream)的内容复制到输出流(OutputStream)的辅助方法。以下是对这个方法进行细分析:
-
参数校验:
Assert.notNull(in, "No InputStream specified"); Assert.notNull(out, "No OutputStream specified");- 使用 Spring Framework 的
Assert工具类,确保输入流和输出流都不为null。如果为null,抛出IllegalArgumentException异常。
- 使用 Spring Framework 的
-
流复制:
try {var2 = StreamUtils.copy(in, out); } finally {close(in);close(out); }- 在
try块中使用StreamUtils.copy方法将输入流的内容复制到输出流。StreamUtils.copy方法是 Spring Framework 提供的用于复制流的实用方法。 - 在
finally块中调用close方法关闭输入流和输出流。这确保在复制完成或发生异常时都会关闭这两个流,避免资源泄漏。
- 在
-
返回复制的字节数:
return var2;- 返回复制的字节数。
StreamUtils.copy方法通常返回复制的字节数,这里将其作为方法的返回值。
- 返回复制的字节数。
综合而言,这个方法是一个简化的输入流到输出流的复制操作,确保在完成复制或发生异常时关闭输入流和输出流。这种实现方式通常用于避免手动处理流关闭操作,提高代码的简洁性和可读性。
总结
以上就是我们对于若依文件上传接口的一个分析,可能还是比较浅显,由于我也是一个初学者,对于这套优秀的框架掌握尚欠,欢迎大家进行批评指正!
项目源码: https://gitee.com/wanghui1201/FileOperateUtils
可以直接拿去当做轮子用,大家做毕设啥的可以直接用,简单好用,就不用大家自己拆离了!
相关文章:
分析若依的文件上传处理逻辑
分析若依的文件上传处理逻辑 注:已经从若依框架完成拆分,此处单独分析一下人家精彩的封装,也来理解一下怎么做一个通用的上传接口!如有分析的,理解的不透彻的地方,大家多多包含,欢迎批评指正&am…...
Note3---初阶二叉树~~
目录 前言🍄 1.树概念及结构☎️ 1.1 树的概念🎄 1.2 树的相关概念🦜 1.2.1 部分概念的加深理解🐾 1.2.2 树与非树🪴 1.3 树的表示🎋 1.4 树在实际中的运用(表示文件系统…...
ElasticSearch学习篇8_Lucene之数据存储(Stored Field、DocValue、BKD Tree)
前言 Lucene全文检索主要分为索引、搜索两个过程,对于索引过程就是将文档磁盘存储然后按照指定格式构建索引文件,其中涉及数据存储一些压缩、数据结构设计还是很巧妙的,下面主要记录学习过程中的StoredField、DocValue以及磁盘BKD Tree的一些…...
ROS机器人入门
http://www.autolabor.com.cn/book/ROSTutorials/ 1、ROS简介 ROS 是一个适用于机器人的开源的元操作系统。其实它并不是一个真正的操作系统,其 底层的任务调度、编译、寻址等任务还是由 Linux 操作系统完成,也就是说 ROS 实际上是运 行在 Linux 上的次级…...
30. 深度学习进阶 - 池化
Hi,你好。我是茶桁。 上一节课,我们详细的学习了卷积的原理,在这个过程中给大家讲了一个比较重要的概念,叫做input channel,和output channel。 当然现在不需要直接去实现, 卷积的原理PyTorch、或者TensorFlow什么的…...
工业应用新典范,飞凌嵌入式FET-D9360-C核心板发布!
来源:飞凌嵌入式官网 当前新一轮科技革命和产业变革突飞猛进,工业领域对高性能、高可靠性、高稳定性的计算需求也在日益增长。为了更好地满足这一需求,飞凌嵌入式与芯驰科技(SemiDrive)强强联合,基于芯驰D9…...
Webrtc 学习交流
花了几周的时间研究了一下webrtc ,并开发了一个小项目,用来点对点私密聊天 交流传输文件等…后续会继续扩展其功能。 体验地址,大狗子的ID,我在线时可以连接测试到我 f3e0d6d0-cfd7-44a4-b333-e82c821cd927 项目特点 除了交换信令与stun 没…...
华为云之轻松搭建 Nginx 静态网站
华为云之轻松搭建 Nginx 静态网站 一、本次实践介绍1. 本次实践目的2. 本次实践环境 二、ECS弹性云服务器介绍三、准备实践环境1. 预置环境2. 查看ECS服务器的账号密码信息3. 登录华为云4. 远程登录ECS服务器 四、安装配置 Nginx1. 安装nginx2. 启动nginx3. 浏览器中访问nginx服…...
【pytorch】图像运行过程中,保证梯度情况下变换
部分操作是危险的,会中断梯度流。 self.patch_transformer(adv_patch, lab_batch, img_size, do_rotateTrue, rand_locFalse)p_img_batch self.patch_applier(img_batch, adv_batch_t) # torch.Size([56, 3, 329, 416])可行危险操作 torch.clamp(adv_batch, 0…...
学习Java第70天,过滤器Filter简介
过滤器概述 Filter,即过滤器,是JAVAEE技术规范之一,作用目标资源的请求进行过滤的一套技术规范,是Java Web项目中最为实用的技术之一 Filter接口定义了过滤器的开发规范,所有的过滤器都要实现该接口 Filter的工作位置是项目中所有目标资源之前,容器在创建HttpServletRequest和…...
Ubuntu Desktop 22.04 设置 ssh 超时时间
Ubuntu Desktop 22.04 使用 ssh 连接服务器时,发现一段时间不操作就会自动断开连接,解决方法如下: 打开 /etc/ssh/ssh_config 文件: sudo vim /etc/ssh/ssh_config在文件最后添加: # ssh 客户端会每隔 30 秒发送一个…...
【微服务】Spring Aop原理深入解析
目录 一、前言 二、aop概述 2.1 什么是AOP 2.2 AOP中的一些概念 2.2.1 aop通知类型 2.3 AOP实现原理 2.3.1 aop中的代理实现 2.4 静态代理与动态代理 2.4.1 静态代理实现 三、 jdk动态代理与cglib代理 3.1 jdk动态代理 3.1.1 jdk代理示例 3.1.2 jdk动态代理模拟实现…...
Spring Boot JSON中文文档
本文为官方文档直译版本。原文链接 Spring Boot JSON中文文档 引言Jackson自定义序列化器和反序列化器混入 GsonJSON-B 引言 Spring Boot 提供与三个 JSON 映射库的集成: GsonJacksonJSON-B Jackson 是首选的默认库。 Jackson Spring-boot-starter-json 提供了…...
Flink系列之:State Time-To-Live (TTL)
Flink系列之:State Time-To-Live TTL 一、TTL二、TTL实现代码三、过期状态的清理 一、TTL Flink的TTL(Time-To-Live)是一种数据过期策略,用于指定数据在流处理中的存活时间。TTL可以应用于Flink中的状态或事件时间窗口࿰…...
数据结构(Chapter Two -01)—线性表及顺序表
2.1 线性表 线性表是具有相同数据类型的n个数据元素的有限序列。第一个元素为表头元素,最后一个元素为表尾元素。除第一个元素,每个元素有且仅有一个直接前驱。除最后一个元素,每个元素都仅有一个直接后继。 其中线性表包括以下(…...
【刷题笔记1】
笔记1 string s;while(cin>>s);cout<<s.length()<<endl;输入为hello nowcoder时,输出为8 (nowcoder的长度) 2.字符串的输入(有空格) string a;getline(cin, a);cout<<a<<endl;输入为ABCabc a 输出为ABCabc a …...
视频数据卡设计方案:120-基于PCIe的视频数据卡
一、产品概述 基于PCIe的一款视频数据收发卡,并通过PCIe传输到存储计算服务器,实现信号的采集、分析、模拟输出,存储。 产品固化FPGA逻辑,实现PCIe的连续采集,单次采集容量2GB,开源的PCIe QT客…...
Windows使用VNC Viewer远程桌面Ubuntu【内网穿透】
文章目录 前言1. ubuntu安装VNC2. 设置vnc开机启动3. windows 安装VNC viewer连接工具4. 内网穿透4.1 安装cpolar【支持使用一键脚本命令安装】4.2 创建隧道映射4.3 测试公网远程访问 5. 配置固定TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址5.3 测试…...
javascript 数组处理的两个利器: `forEach` 和 `map`(上)
🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云…...
【C语言】SCU安全项目1-FindKeys
目录 前言 命令行参数 16进制转字符串 extract_message1 process_keys12 extract_message2 main process_keys34 前言 因为这个学期基本都在搞CTF的web方向,C语言不免荒废。所幸还会一点指针相关的知识,故第一个安全项目做的挺顺利的,…...
新手福音:免安装claude code,在快马平台开启你的ai编程第一课
作为一个刚接触编程的新手,最近想尝试用AI辅助写代码,但光是安装本地工具就让我头疼不已。直到发现了InsCode(快马)平台,才发现原来AI编程可以这么简单——不用配环境、不用解决依赖冲突,打开网页就能直接开玩。今天就把我的入门体…...
源网荷储全场景适配:新型电力系统时序数据库落地指南
新型电力系统应该用什么数据库?源网荷储四侧的时序数据库选型与落地实战 “双碳”目标的推进正在深刻重构电力系统的运行逻辑。新能源装机占比持续攀升,储能、虚拟电厂、需求响应等新业态快速涌现,源、网、荷、储各侧的角色与互动方式正在被…...
SemanticKITTI数据集评测:DarkNet53Seg、PointNet++等模型谁更强?附复现代码
SemanticKITTI点云语义分割实战:模型选型与性能优化指南 点云语义分割技术正在重塑自动驾驶、机器人导航和三维场景理解等领域的研究范式。作为该领域最具挑战性的基准之一,SemanticKITTI数据集凭借其大规模、高密度标注和真实场景多样性,已成…...
HunyuanVideo-Foley保姆级教程:从零部署到音效生成的5个关键步骤
HunyuanVideo-Foley保姆级教程:从零部署到音效生成的5个关键步骤 1. 环境准备与镜像部署 1.1 硬件要求检查 在开始部署前,请确保您的设备满足以下最低配置要求: 显卡:NVIDIA RTX 4090/4090D(24GB显存)内…...
告别‘缺少DLL’:用EnigmaVB给Qt5.14程序封包的保姆级避坑指南
告别“缺少DLL”困境:EnigmaVBQt5.14封包全流程实战手册 当你用Qt Creator完成开发,满怀期待地将程序打包发给用户,却收到“缺少xxx.dll”的报错反馈时,这种挫败感开发者都深有体会。本文将以Qt5.14为例,结合EnigmaVB封…...
目标检测新手必看:如何用Python手写IOU计算函数(附完整代码)
目标检测实战:从零编写Python版IOU计算函数 刚接触目标检测时,最让人困惑的莫过于那些神秘的评估指标。其中IOU(交并比)就像一把尺子,能量化算法预测框与真实框的贴合程度。但纸上得来终觉浅,今天我们就用P…...
零基础养龙虾:OpenClaw部署从入门到上手,一篇讲透!
2026年,OpenClaw(昵称 “龙虾”)凭借 “能真正动手干活” 的核心能力,成为开源AI Agent领域的顶流。它不仅能像ChatGPT一样聊天,更能自主操作电脑——整理文件、控制浏览器、发送邮件、甚至调用硬件设备。因其图标酷似…...
Kettle错误处理实战:如何用表输出步骤捕获并存储ETL过程中的异常数据
Kettle错误处理实战:如何用表输出步骤捕获并存储ETL过程中的异常数据 在数据仓库和ETL(Extract, Transform, Load)流程中,错误处理是确保数据质量的关键环节。Kettle(现称Pentaho Data Integration)作为一款…...
GLM-OCR开发者实操手册:Gradio client调用+批量图片识别脚本示例
GLM-OCR开发者实操手册:Gradio client调用批量图片识别脚本示例 你是不是也遇到过这样的场景:手头有一堆发票、合同或者产品说明书图片,需要把里面的文字、表格甚至公式都提取出来?一张张手动录入或者用传统OCR工具,不…...
Web AR技术深度探秘:7个创新案例重构浏览器增强现实体验
Web AR技术深度探秘:7个创新案例重构浏览器增强现实体验 【免费下载链接】AR.js Image tracking, Location Based AR, Marker tracking. All on the Web. 项目地址: https://gitcode.com/gh_mirrors/arj/AR.js 你是一个文章写手,你负责为开源项目…...
