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

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

在这里插入图片描述

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

控制层代码剖析

    @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

可以直接拿去当做轮子用,大家做毕设啥的可以直接用,简单好用,就不用大家自己拆离了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/223397.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LabVIEW在高铁温度与振动监测中的应用

​LabVIEW在高铁温度与振动监测中的应用 高速铁路的可靠性和安全性是现代铁路运输系统设计和运营的重中之重。LabVIEW软件作为一个多功能、可扩展的图形编程环境,提供了一个理想的平台,用于开发高铁监测系统,不仅监测实时数据,也…

C# WPF上位机开发(属性页面的设计)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 在软件开发中,属性或者参数设置是很重要的一个部分。这个时候如果不想通过动态添加控件的方法来处理的话,那么可以通过tab控…

jrebel debug 启动不起来

idea更新之后jrebel debug模式启动不起来。 将下面的设置取消之后就可以了,希望能帮到你们… 被卡了两天… jrebel信息。 idea IntelliJ IDEA 2023.3.1 (Ultimate Edition) Build #IU-233.11799.300, built on December 12, 2023 Licensed to Alexandra Martin…

GitHub Universe 2023 Watch Party in Shanghai:在开源世界中找到真我

文章目录 ⭐ 前言⭐ “我”的开源之旅⭐ 为什么要做开源⭐ 要如何做好开源⭐ 开源的深度影响⭐ 小结 ⭐ 前言 周末有幸参加了在上海举行的 GitHub Universe 2023 Watch Party,这是一个充满激情和活力的开源开发者日。我有幸聆听了一场特别令人印象深刻的演讲&#…

Python接口测试框架选择之pytest+yaml+Allure!

一、为什么选择pytest? pytest完全兼容python自带的unittest pytest让单元测试更简单,能很好的管理测试用例。 对于实现接口测试的复杂场景,pytest的fixture、PDB等高阶用法都能实现需求。 入门简单,对于代码基础薄弱的团队人员…

Mysql - 常用插入数据的三种方法详解及练习

目录 🥙8.1.1 mysql中常用的三种插入数据的语句 1. insert into - 插入数据 2. replace into - 插入替换数据 3. insert ignore - 如果已存在,忽略当前新数据 🥙8.1.2 以上三种方法的练习及区分 🥙8.1.3 说明 &#x1f959…

高项备考葵花宝典-项目进度管理核心方法加强理解-关键路径法

关键路径法(Critical Path Method,CPM)是一种基于数学计算的项目计划管理方法,是网络图计划方法的一种,属于肯定型的网络图。关键路径法将项目分解成为多个独立的活动并确定每个活动的工期,然后用逻辑关系&…

YOLOv8-Seg改进:UniRepLKNetBlock 助力分割 | UniRepLKNet,通用感知大内核卷积网络, 2023.12

🚀🚀🚀本文改进: UniRepLKNet,通用感知大内核卷积网络,ImageNet-22K预训练,精度 和速度SOTA,ImageNet达到88%, COCO达到56.4 box AP,ADE20K达到55.6 mIoU UniRepLKNetBlock 与C2f进行结合使用 🚀🚀🚀YOLOv8-seg创新专栏:http://t.csdnimg.cn/KLSdv 学姐带…

关于set和map的简单理解

1. 关于搜索 1.1 set和map的引入 Map和set是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关。以前常见的搜索方式有: 1. 直接遍历,时间复杂度为O(N),元素如果比较多效率会非常慢 2. 二分查找&…

设计模式详解---抽象工厂模式

继续前言,工厂模式中抽象工厂模式的讲解: 1. 前面的工厂模式有啥问题? 前面的工厂模式有这么个问题:一个产品就给了一个工厂,这样子如果产品变多,系统就会很复杂: 2. 解决方法 我们可以按照手…

DSP280049C初学(4)-FLASH烧录以及部分程序转移至RAM运行

DSP280049C初学(4)-FLASH烧录以及部分程序转移至RAM运行 实现目的:代码在RAM中调试完成后,就需要将其固化下载到FLASH中,但是FLASH中运行所有程序的话会存在计算或程序运行时间过长的问题,故还需要将部分代…

Sublime Text 3 解决中文乱码问题

Sublime Text 3 解决中文乱码问题 一、引言二、主要内容1. 初识 ConvertToUTF8 插件2. 安装 ConvertToUTF8 插件3. 中文乱码问题解决 三、总结 一、引言 在 《Sublime Text 3配置C/C开发环境》 博文中,Huazie 带大家利用 Sublime Text 3 配置了 C/C 开发环境&#…

【infiniband】安装驱动

安装 InfiniBand 驱动包通常包括几个步骤,但是可能会有所不同,取决于使用的操作系统和具体的硬件。以下是在 Linux 系统上安装 InfiniBand 驱动的通用步骤: 1.检查硬件和操作系统兼容性 确保服务器硬件支持 InfiniBand,并查看驱动…

LeetCode Hot100 23.合并K个升序链表

题目: 给你一个链表数组,每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中,返回合并后的链表。 方法:分治,类似于归并 class Solution {public ListNode mergeKLists(ListNode[] lists) {return mer…

arp欺骗原理以及实现方式

我们知道了arp的作用,那么此时我们怎么可以用他来进行攻击呢?在一个局域网中,我们怎么实现呢? 原理: 这样B就可以做到中间人了,可以接受到两个主机的数据了。换句话来说,在同一个局域网内&…

C/C++ 有效的括号判断

题目: 给定一个只包括 (,),{,},[,]的字符串s,判断字符串是否有效。 约束: 有效字符串需满足: 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。…

【六】python观察者设计模式

6.1行为型模式简介 观察者设计模式是最简单的行为型模式之一,所以我们先简单了解一下行为型模式 创建型模式的工作原理是基于对象的创建机制的。由于这些模式隔离了对象的创建细 节,所以使得代码能够与要创建的对象的类型相互独立。结构型模式用于设计对象和类的结…

Python GUI教程:轻松构建用户界面

大家好,从网络开发到数据科学,Python被广泛应用于各个领域。本文将探索Python内置的用于创建图形用户界面(GUI)的库:Tkinter。无论是初学者还是经验丰富的开发者,了解如何创建Python GUI都可以增强构建交互…

Axie Infinity 之后,Ronin 的潜力何在?

作者:stellafootprint.network 数据来源:Ronin Dashboard 备受欢迎的 Web3 游戏 Pixels 在 2023 年 10 月下旬从 Polygon 迁移到了专为游戏设计的区块链 Ronin。Pixels 此前作为 Polygon 上活跃用户(钱包数量)最多的 Web3 游戏&…

iPhone手机中备忘录如何改变字体颜色

作为一名iPhone用户,我经常使用手机备忘录来记录生活中的点点滴滴。这样,我的大脑就能从繁琐的记忆任务中解脱出来,专注于更重要的事情。 而且,我有一个特别的习惯,那就是使用不同颜色的字体来区分不同的备忘录。这样…