当前位置: 首页 > news >正文

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

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

在这里插入图片描述

注:已经从若依框架完成拆分,此处单独分析一下人家精彩的封装,也来理解一下怎么做一个通用的上传接口!如有分析的,理解的不透彻的地方,大家多多包含,欢迎批评指正,但是请不要恶语相向!

控制层代码剖析

    @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());}}
  1. @PostMapping("/upload"):这是一个用于处理HTTP POST请求的注解,它将请求映射到/upload路径。在这里,它用于处理文件上传请求。

  2. public AjaxResult uploadFile(MultipartFile file):这是处理文件上传的方法。它接收一个MultipartFile对象,这是Spring提供的用于处理文件上传的类。

  3. String filePath = RuoYiConfig.getUploadPath();:获取文件上传路径,通过RuoYiConfig.getUploadPath()方法获取,是从配置文件中读取的上传路径。

    server:port: 8888
    # 项目相关配置
    ruoyi:# 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)profile: ./
  4. String fileName = FileUploadUtils.upload(filePath, file);:调用FileUploadUtils.upload方法实现文件上传,该方法包含文件存储逻辑,根据传入的文件路径和MultipartFile对象,返回新的文件名。下一标题我们将会着重对于这个方法进行解析。

  5. String url = serverConfig.getUrl() + fileName;:构造文件的访问URL,是通过拼接服务器的URL和上传后的文件名得到的。

  6. AjaxResult ajax = AjaxResult.success();:创建一个成功的AjaxResult对象,用于封装返回给客户端的数据。

  7. ajax.put("url", url);:将文件的访问URL放入AjaxResult中,以便客户端获取上传后的文件的访问地址。

  8. ajax.put("fileName", fileName);:将上传后的文件名放入AjaxResult中。

  9. ajax.put("newFileName", FileUtils.getName(fileName));:将上传后的文件名去除路径的部分,只保留文件名放入AjaxResult中。

  10. ajax.put("originalFilename", file.getOriginalFilename());:将原始文件名放入AjaxResult中。

  11. return ajax;:返回封装了文件相关信息的AjaxResult对象,向客户端提供文件上传成功的响应。

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

这是一个文件上传工具类,主要用于处理文件上传的相关逻辑。

  1. 常量定义:

    • DEFAULT_MAX_SIZE:默认文件大小限制为50MB。
    • DEFAULT_APP_MAX_SIZE:默认App文件大小限制为200MB。
    • DEFAULT_FILE_NAME_LENGTH:默认文件名最大长度为100字符。
    • defaultBaseDir:默认的文件上传基目录,初始化时可能从RuoYiConfig中获取。
  2. 上传文件方法:

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

      由于这段代码是文件上传的核心方法,我们对其进行细分析:

      1. 文件名长度检查:

        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异常。
      2. 文件扩展名和大小校验:

        assertAllowed(file, allowedExtension);
        
        • 调用assertAllowed方法,对文件的扩展名和大小进行校验。

        • 如果不符合要求,会抛出FileSizeLimitExceededExceptionInvalidExtensionException等异常。

          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,以下是对其进行细分析:

          1. 获取文件大小和扩展名:

            long size = file.getSize();
            String fileSuffix;
            if (null != file.getOriginalFilename() && file.getOriginalFilename().contains(".")) {fileSuffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")+1);
            } else {fileSuffix = null;
            }
            
            • 获取上传文件的大小和原始文件名中的扩展名。
          2. 文件大小校验:

            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 异常。
          3. 文件扩展名校验:

            String fileName = file.getOriginalFilename();
            String extension = getExtension(file);
            if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {// ...
            }
            
            • 获取上传文件的原始文件名和文件扩展名。
            • 如果 allowedExtension 不为空且文件扩展名不在允许的扩展名列表中,进入后续的异常判断逻辑。
          4. 根据文件类型抛出不同的异常:

            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 方法主要用于对文件的大小和扩展名进行校验,确保文件满足预定义的条件,否则抛出相应的异常。

      3. 生成文件名和绝对路径:

        String fileName = extractFilename(file);
        String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
        
        • 调用extractFilename方法,根据上传文件生成编码后的文件名。
        • 调用getAbsoluteFile方法,获取文件的绝对路径。
      4. 文件写入磁盘:

        file.transferTo(Paths.get(absPath));
        
        • 使用transferTo方法将文件写入磁盘,具体路径由absPath决定。
      5. 返回文件相对路径:

        return getPathFileName(baseDir, fileName);
        
        • 调用getPathFileName方法,生成相对于上传基目录的文件路径。

      所以,综上所述呢,这段代码通过一系列步骤完成了文件上传的核心逻辑,包括文件名长度、扩展名、大小的校验,生成文件名,将文件写入磁盘,并返回相对路径。异常的处理确保了在上传过程中出现问题时能够向上层抛出相应的异常。

  3. 文件名处理:

    • 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));
    }
    
    1. 构造文件名:

      StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
      
      • 使用字符串格式化工具(可能是自定义的StringUtils.format方法)构建文件名。这个文件名包括以下几个部分:
        • {}/:日期路径,可能是根据上传日期生成的目录结构。
        • {}_:原始文件名去除扩展名后的部分。
        • {}:通过某种方式获取的上传序列号或ID。
        • .:文件名的分隔符。
        • getExtension(file):获取文件的扩展名。
    2. 返回构造的文件名:

      return StringUtils.format(...);
      
      • 返回构造好的文件名。

    所以,综上所述呢,extractFilename 方法主要用于根据上传文件的信息构建一个新的文件名,其中包括日期路径、原始文件名的基本部分、上传序列号或ID,以及文件扩展名。这个文件名通常用于确定上传文件在服务器上的存储位置。

  4. 文件路径操作:

    • 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 方法,以下是对其进行细分析:

      1. 构造文件对象:

        File desc = new File(uploadDir + File.separator + fileName);
        
        • 创建一个 File 对象,表示上传目录 (uploadDir) 下的特定文件 (fileName)。
      2. 检查文件是否存在:

        if (!desc.exists()) {// ...
        }
        
        • 判断文件是否已经存在。如果文件不存在,执行后续的逻辑。
      3. 创建文件父目录:

        if (!desc.getParentFile().exists()) {desc.getParentFile().mkdirs();
        }
        
        • 如果文件的父目录不存在,调用 mkdirs() 方法创建父目录。这样可以确保文件存储路径的所有父目录都存在。
      4. 返回文件对象:

        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;}
      
  5. 文件大小和扩展名校验:

    • assertAllowed(MultipartFile file, String[] allowedExtension):对上传的文件进行大小和扩展名的校验,根据文件扩展名调用isAllowedExtension方法判断是否允许上传。
    • isAllowedExtension(String extension, String[] allowedExtension):判断文件扩展名是否在允许的扩展名列表中。
  6. 其他方法:

    • 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 对象,表示文件路径。下面是一些关键的点:

  1. 创建输出流:

    Path dest = ...; // 指定文件路径
    OutputStream outputStream = Files.newOutputStream(dest);
    
    • 使用 Files.newOutputStream(dest) 创建一个输出流。
  2. 写入数据:

    byte[] data = ...; // 准备写入的数据
    outputStream.write(data);
    
    • 利用得到的输出流,可以使用 write 方法将数据写入文件。
  3. 关闭流:

    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)的辅助方法。以下是对这个方法进行细分析:

  1. 参数校验:

    Assert.notNull(in, "No InputStream specified");
    Assert.notNull(out, "No OutputStream specified");
    
    • 使用 Spring Framework 的 Assert 工具类,确保输入流和输出流都不为 null。如果为 null,抛出 IllegalArgumentException 异常。
  2. 流复制:

    try {var2 = StreamUtils.copy(in, out);
    } finally {close(in);close(out);
    }
    
    • try 块中使用 StreamUtils.copy 方法将输入流的内容复制到输出流。StreamUtils.copy 方法是 Spring Framework 提供的用于复制流的实用方法。
    • finally 块中调用 close 方法关闭输入流和输出流。这确保在复制完成或发生异常时都会关闭这两个流,避免资源泄漏。
  3. 返回复制的字节数:

    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中的状态或事件时间窗口&#xff0…...

数据结构(Chapter Two -01)—线性表及顺序表

2.1 线性表 线性表是具有相同数据类型的n个数据元素的有限序列。第一个元素为表头元素,最后一个元素为表尾元素。除第一个元素,每个元素有且仅有一个直接前驱。除最后一个元素,每个元素都仅有一个直接后继。 其中线性表包括以下(…...

【刷题笔记1】

笔记1 string s;while(cin>>s);cout<<s.length()<<endl;输入为hello nowcoder时&#xff0c;输出为8 &#xff08;nowcoder的长度&#xff09; 2.字符串的输入(有空格) string a;getline(cin, a);cout<<a<<endl;输入为ABCabc a 输出为ABCabc a …...

视频数据卡设计方案:120-基于PCIe的视频数据卡

一、产品概述 基于PCIe的一款视频数据收发卡&#xff0c;并通过PCIe传输到存储计算服务器&#xff0c;实现信号的采集、分析、模拟输出&#xff0c;存储。 产品固化FPGA逻辑&#xff0c;实现PCIe的连续采集&#xff0c;单次采集容量2GB&#xff0c;开源的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`(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…...

【C语言】SCU安全项目1-FindKeys

目录 前言 命令行参数 16进制转字符串 extract_message1 process_keys12 extract_message2 main process_keys34 前言 因为这个学期基本都在搞CTF的web方向&#xff0c;C语言不免荒废。所幸还会一点指针相关的知识&#xff0c;故第一个安全项目做的挺顺利的&#xff0c…...

IDA pro软件 如何修改.exe小程序打开对话框显示的文字?

环境: Win10 专业版 IDA pro Version 7.5.201028 .exe小程序 问题描述: IDA pro软件 如何修改.exe小程序打开对话框显示的文字? 解决方案: 一、在IDA Python脚本中编写代码来修改.rdata段中的静态字符串可以使用以下示例代码作为起点(未成功) import idc# 定义要修…...

Ubuntu22.04切换用户

一、只有一个用户时没有切换用户菜单项 1、用户信息 cat /etc/passwd 2、系统菜单 二、添加用户 添加新用户ym&#xff0c;全名yang mi 三、有两个及以上的用户时出现切换用户菜单项 1、用户信息 cat /etc/passwd 2、系统菜单 四、切换用户 1、点击上图中Switch User …...

torch.gather(...)

1. Abstract 对于 pytorch 中的函数 torch.gather(input, # (Tensor) the source tensordim, # (int) the axis along which to indexindex, # (LongTensor) the indices of elements to gather*,sparse_gradFalse,outNone ) → Tensor有点绕&#xff0c;很多博客画各…...

vscode如何开发微信小程序?JS与TS的主要区别?

要在 VS Code 中编写微信小程序代码并同步到 Git&#xff0c;需要安装以下插件&#xff1a; 1. 微信小程序插件&#xff08;WeChat Mini Program&#xff09;&#xff1a;此插件提供了微信小程序的语法高亮、代码提示、调试、上传等功能。 2. Git 插件&#xff08;GitLens、…...

产品入门第五讲:Axure交互和情境

目录 一.Axure交互和情境的介绍 1.交互介绍 概念 常见的Axure交互设计技巧 2.情境介绍 概念 常见的Axure情境设计技巧&#xff1a; 二.实例展示 1.ERP登录页到主页的跳转 2.ERP的菜单跳转到各个页面 &#x1f4da;&#x1f4da; &#x1f3c5;我是默&#xff0c;一个…...

Python 自动化之收发邮件(一)

imapclient / smtplib 收发邮件 文章目录 imapclient / smtplib 收发邮件前言一、基本内容二、发送邮件1.整体代码 三、获取邮件1.整体代码 总结 前言 简单给大家写个如何用Python进行发邮件和查看邮件教程&#xff0c;希望对各位有所帮助。 一、基本内容 本文主要分为两部分…...

Flutter开发笔记 —— sqflite插件数据库应用

前言 今天在观阅掘金大佬文章的时候&#xff0c;了解到了该 sqflite 插件&#xff0c;结合官网教程和自己实践&#xff0c;由此总结出该文&#xff0c;希望对大家的学习有帮助&#xff01; 插件详情 Flutter的 SQLite 插件。支持 iOS、Android 和 MacOS。 支持事务和batch模式…...

OxLint 发布了,Eslint 何去何从?

由于最近的rust在前端领域的崛起&#xff0c;基于rust的前端生态链遭到rust底层重构&#xff0c;最近又爆出OxLint&#xff0c;是一款基于Rust的linter工具Oxlint在国外前端圈引起热烈讨论&#xff0c;很多大佬给出了高度评价&#xff1b;你或许不知道OxLint&#xff0c;相比ES…...

第一次使用ThreadPoolExecutor处理业务

通过对业务逻辑的分析&#xff0c;进行编码&#xff0c;先把第一条sql查出来的数据进行分组&#xff0c;然后分别使用不同的线程去查询数据返回&#xff0c;并添加到原来的数据中。 总感觉哪里写的不对&#xff0c;但是同事们都没用过这个&#xff0c;请大家指教一下&#xff…...

Sharding-Jdbc(6):Sharding-Jdbc日志分析

1 修改配置 将配置文件中的开启分片日志从false改为true Sharding-JDBC中的路由结果是通过分片字段和分片方法来确定的,如果查询条件中有 id 字段的情况还好&#xff0c;查询将会落到某个具体的分片&#xff1b;如果查询没有分片的字段&#xff0c;会向所有的db或者是表都会查…...

wordpress标签字段/如何自己搭建一个网站

学生信息成绩管理系统采用Python qt5 来实现 快速获得整个项目的入口&#xff1a;https://download.csdn.net/download/nanxiang11/15179227 就可以获取整个项目资源 由于项目文件比较多所以我分了好几个包来完成这个项目如下图&#xff1a; 然后就是一些要安装的组件 其实…...

做外挂网站空间/百度长尾关键词挖掘

给定两个整数数组&#xff08;第一个是数组 A&#xff0c;第二个是数组 B&#xff09;&#xff0c;在数组 A 中取 A[i]&#xff0c;数组 B 中取 B[j]&#xff0c;A[i] 和 B[j]两者的差越小越好(|A[i] - B[j]|)。返回最小差。样例给定数组 A [3,4,6,7]&#xff0c; B [2,3,8,9…...

网站more应该怎么做/自媒体135的网站是多少

msgServer集群&#xff08;ms&#xff09; 设计作用&#xff1a; 该服务主要提供客户端的接入服务。每个ms维护当前接入用户的用户状态和心跳&#xff0c;每个ms地位是对等的。 设计考虑&#xff1a; ms之间不会直接交互&#xff0c;减少交互的复杂度。在用户数量增大的情况&am…...

淘宝客商城网站建设/bt磁力链好用的引擎

server.xml配置数据帐号和密码等...

成都网站排名 生客seo/搜一搜排名点击软件

资料来源&#xff1a;MySQL 教程 目录1 相关术语2 安装3 库级别的操作指令4 更改数据库名1 相关术语 数据库: 数据库是一些关联表的集合。表: 表是数据的矩阵。在一个数据库中的表看起来像一个简单的电子表格。列: 一列(数据元素) 包含了相同类型的数据, 例如邮政编码的数据。…...

公司商城网站建设方案/cps广告联盟网站

因为之前一直在服务器上直接操作mysql&#xff0c;所以没有遇到过什么问题&#xff0c;今天开发突然要测试个功能&#xff0c;需要连接数据库&#xff0c;发现一直连接不上&#xff01;因为时间过得比较久mysql的root密码已经忘记1.linux下更改root密码vim /etc/my.cnf在[mysql…...