【问题标题】:How to convert a multipart file to File?如何将多部分文件转换为文件?
【发布时间】:2014-08-11 23:05:57
【问题描述】:

谁能告诉我将多部分文件 (org.springframework.web.multipart.MultipartFile) 转换为文件 (java.io.File) 的最佳方法是什么?

在我的 spring mvc web 项目中,我将上传的文件作为 Multipart 文件。我必须将其转换为 File(io),因此我可以调用此图像存储服务(Cloudinary) .他们只接受类型(文件)。

我进行了很多搜索但都失败了。如果有人知道一个好的标准方法,请告诉我? 谢谢

【问题讨论】:

  • 有什么东西阻止你使用MultipartFile.transferTo()这个方法吗?

标签: java spring spring-mvc file-upload cloudinary


【解决方案1】:

您可以使用getBytes 方法获取MultipartFile 的内容,您可以使用Files.newOutputStream() 写入文件:

public void write(MultipartFile file, Path dir) {
    Path filepath = Paths.get(dir.toString(), file.getOriginalFilename());

    try (OutputStream os = Files.newOutputStream(filepath)) {
        os.write(file.getBytes());
    }
}

您也可以使用transferTo method:

public void multipartFileToFile(
    MultipartFile multipart, 
    Path dir
) throws IOException {
    Path filepath = Paths.get(dir.toString(), multipart.getOriginalFilename());
    multipart.transferTo(filepath);
}

【讨论】:

  • 我使用了 transferTo 函数,但我觉得有问题。就像它为本地机器保存临时文件一样。
  • @Ronnie 我也有同样的问题。您找到任何解决方法了吗?
  • org.apache.commons.io.FileUtils.deleteQuietly(convFile.getParentFile()); ,这应该删除临时文件@Ronnie
  • createNewFIle() 在这里既无意义又浪费。您现在有义务 new FileOutputStream()(通过操作系统)删除如此创建的文件创建一个新文件。
  • @Petros Tsialiamanis 在 Java 中是否有任何文件转换大小限制。假设我正在使用 3GB 的文件。
【解决方案2】:

您还可以使用Apache Commons IO 库和FileUtils class。如果您使用的是 maven,则可以使用上述依赖项加载它。

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>

MultipartFile 的来源保存到磁盘。

File file = new File(directory, filename);

// Create the file using the touch method of the FileUtils class.
// FileUtils.touch(file);

// Write bytes from the multipart file to disk.
FileUtils.writeByteArrayToFile(file, multipartFile.getBytes());

【讨论】:

  • FileUtils.touch() 在这里既无意义又浪费。您现在有义务 new FileOutputStream()(通过操作系统)删除如此创建的文件创建一个新文件。
  • 感谢您的评论。我检查了方法 FileUtils.writeByteArrayToFile 的来源。我认为这种方法不会在文件存在的情况下重新创建文件(2.4 版)。 multipartFile 对象包括我们想要存储在文件系统某处的上传文件的字节。我的目的是将此字节存储到首选位置。我保留 FileUtils.touch 方法的唯一原因是要明确这是一个新文件。 FileUtils.writeByteArrayToFile 会在文件不存在时创建文件(和完整路径),因此不需要 FileUtils.touch。
【解决方案3】:

如果接口MultipartFile的类是CommonsMultipartFile,则可以通过强制转换访问Spring中的临时文件。

public File getTempFile(MultipartFile multipartFile)
{
    CommonsMultipartFile commonsMultipartFile = (CommonsMultipartFile) multipartFile;
    FileItem fileItem = commonsMultipartFile.getFileItem();
    DiskFileItem diskFileItem = (DiskFileItem) fileItem;
    String absPath = diskFileItem.getStoreLocation().getAbsolutePath();
    File file = new File(absPath);

    //trick to implicitly save on disk small files (<10240 bytes by default)
    if (!file.exists()) {
        file.createNewFile();
        multipartFile.transferTo(file);
    }

    return file;
}

要摆脱文件小于 10240 字节的技巧,maxInMemorySize 属性可以在 @Configuration @EnableWebMvc 类中设置为 0。之后,所有上传的文件都将存储在磁盘上。

@Bean(name = "multipartResolver")
    public CommonsMultipartResolver createMultipartResolver() {
        CommonsMultipartResolver resolver = new CommonsMultipartResolver();
        resolver.setDefaultEncoding("utf-8");
        resolver.setMaxInMemorySize(0);
        return resolver;
    }

【讨论】:

  • createNewFIle() 在这里既无意义又浪费。您现在有义务 new FileOutputStream()(通过操作系统)删除如此创建的文件创建一个新文件。
  • @EJP 是的,这毫无意义,现在我修复了编辑时犯的这个错误。但是 createNewFIle() 并不浪费,因为如果 CommonsMultipartFile 小于 10240 字节,则不会创建文件系统中的文件。所以应该在 FS 中创建一个具有任何唯一名称的新文件(我使用的是 DiskFileItem 的名称)。
  • @Alex78191 隐式保存在磁盘上的小文件是什么意思(默认情况下
  • @AnandTagore 我的意思是 MultipartFile 小于 10240 字节的内容没有保存在文件系统中,所以应该手动创建文件。
【解决方案4】:

虽然接受的答案是正确的,但如果您只是想将图像上传到 cloudinary,还有更好的方法:

Map upload = cloudinary.uploader().upload(multipartFile.getBytes(), ObjectUtils.emptyMap());

multipartFile 是您的 org.springframework.web.multipart.MultipartFile

【讨论】:

    【解决方案5】:

    Alex78191 的回答对我有用。

    public File getTempFile(MultipartFile multipartFile)
    {
    
    CommonsMultipartFile commonsMultipartFile = (CommonsMultipartFile) multipartFile;
    FileItem fileItem = commonsMultipartFile.getFileItem();
    DiskFileItem diskFileItem = (DiskFileItem) fileItem;
    String absPath = diskFileItem.getStoreLocation().getAbsolutePath();
    File file = new File(absPath);
    
    //trick to implicitly save on disk small files (<10240 bytes by default)
    
    if (!file.exists()) {
        file.createNewFile();
        multipartFile.transferTo(file);
    }
    
    return file;
    }
    

    对于上传大小超过 10240 字节的文件,请将 multipartResolver 中的 maxInMemorySize 更改为 1MB。

    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- setting maximum upload size t 20MB -->
    <property name="maxUploadSize" value="20971520" />
    <!-- max size of file in memory (in bytes) -->
    <property name="maxInMemorySize" value="1048576" />
    <!-- 1MB --> </bean>
    

    【讨论】:

    • maxInMemorySize 与文件上传大小限制无关。文件上传大小由maxUploadSize 属性设置。
    • 要摆脱文件小于 10240 字节的诡计 maxInMemorySize 属性可以设置为 0
    • @Alex78191 我已经改变了它,它对我有用。我用你的代码来转换文件。所以我更改了 applicationcontext.xml 中的属性以摆脱内存限制。它有效!!!
    • 从多部分文件创建文件时,应将其保存在内存中。所以为此我必须增加 maxInMemorySize。
    【解决方案6】:

    @PetrosTsialiamanis 帖子的小修正, new File( multipart.getOriginalFilename()) 这将在服务器位置创建文件,有时您会遇到用户的写入权限问题,并不总是可以为执行操作的每个用户授予写入权限。 System.getProperty("java.io.tmpdir") 将创建临时目录,您的文件将在其中正确创建。 这样您就可以创建临时文件夹,在其中创建文件,稍后您可以删除文件或临时文件夹。

    public  static File multipartToFile(MultipartFile multipart, String fileName) throws IllegalStateException, IOException {
        File convFile = new File(System.getProperty("java.io.tmpdir")+"/"+fileName);
        multipart.transferTo(convFile);
        return convFile;
    }
    

    将此方法放在您的常用实用程序中,并像使用它一样使用它。 Utility.multipartToFile(...)

    【讨论】:

    • 这应该是正确的答案(尤其是使用 tmp 目录,因为它会解决一些问题,如 linux 中的权限被拒绝)
    • @HoussemBadri 是的,考虑到权限问题等,这是必需的。
    • Linux 用户在这里,只有这个解决方案对我有用。 2021. 谢谢
    【解决方案7】:
      private File convertMultiPartToFile(MultipartFile file ) throws IOException
        {
            File convFile = new File( file.getOriginalFilename() );
            FileOutputStream fos = new FileOutputStream( convFile );
            fos.write( file.getBytes() );
            fos.close();
            return convFile;
        }
    

    【讨论】:

    • 给出这个异常 java.io.FileNotFoundException: multipdf.pdf (Permission denied)
    【解决方案8】:

    MultipartFile.transferTo(File) 很好,但毕竟不要忘记清理临时文件。

    // ask JVM to ask operating system to create temp file
    File tempFile = File.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_POSTFIX);
    
    // ask JVM to delete it upon JVM exit if you forgot / can't delete due exception
    tempFile.deleteOnExit();
    
    // transfer MultipartFile to File
    multipartFile.transferTo(tempFile);
    
    // do business logic here
    result = businessLogic(tempFile);
    
    // tidy up
    tempFile.delete();
    

    在下面查看 Razzlero 关于在 JVM 退出时执行的 File.deleteOnExit() 的评论(这可能非常罕见)。

    【讨论】:

    • deleteOnExit(),它只会在JVM终止时触发,所以在异常时不会触发。因此,在服务器应用程序等长时间运行的应用程序上使用deleteOnExit() 时需要小心。对于服务器应用程序,JVM 很少会退出。所以你需要小心deleteOnExit() 导致内存泄漏。 JVM 需要跟踪它需要在退出时删除的所有文件,这些文件没有被清除,因为 JVM 没有终止。
    • @Razzlero 感谢您指出它仅在 JVM 退出时删除文件。但是,这不是内存泄漏,它按设计工作。
    【解决方案9】:

    如果您不想使用 MultipartFile.transferTo()。你可以这样写文件

        val dir = File(filePackagePath)
        if (!dir.exists()) dir.mkdirs()
    
        val file = File("$filePackagePath${multipartFile.originalFilename}").apply {
            createNewFile()
        }
    
        FileOutputStream(file).use {
            it.write(multipartFile.bytes)
        }
    

    【讨论】:

      【解决方案10】:

      使用 Apache Commons 的单行答案。

      FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), file);

      【讨论】:

        【解决方案11】:

        MultipartFile 可以获取 InputStream。

        multipartFile.getInputStream()

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-03-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-06-08
          • 2016-04-07
          相关资源
          最近更新 更多