如何将多部分文件转换为File?

113

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

在我的Spring MVC Web项目中,我将上传的文件作为Multipart文件获取。我必须将其转换为File(io),以便我可以调用这个图像存储服务(Cloudinary)。他们只接受类型(File)。

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


6
жңүд»Җд№Ҳйҳ»жӯўжӮЁдҪҝз”ЁMultipartFile.transferTo()ж–№жі•зҡ„дёңиҘҝеҗ—пјҹ - fajarkoe
11个回答

206

您可以使用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方法

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

8
我使用了"transferTo"函数,但感觉存在问题,它会将临时文件保留在本地驱动器上。 - Morez
@Ronnie,我也遇到了同样的问题。你有找到任何解决方法吗? - Half Blood Prince
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - kavinder
5
这里使用 createNewFile() 是没有意义且浪费的。现在,你要求操作系统通过 new FileOutputStream() 来删除所创建的文件,并再次创建一个新的文件。 - user207421
@Petros Tsialiamanis,Java中是否有任何文件转换大小限制?比如说我正在使用3GB的文件。 - Rohit
dir变量的值将是什么?似乎不可能在本地存储中没有任何文件的情况下创建文件对象。我是对的吗? - Rakesh L

24

对@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(...)


3
这应该是正确的答案(特别是对于使用tmp目录,因为它可以解决在Linux中出现的一些权限被拒绝的问题)。 - Houssam Badri
1
是的,考虑到权限问题等因素,这是必需的。 - Swadeshi
1
我是Linux用户,只有这个解决方案对我有效。2021年感谢。 - gdogra

21

尽管被接受的答案是正确的,但如果你只是想将图片上传到Cloudinary,还有更好的方法:

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

其中multipartFile是你的org.springframework.web.multipart.MultipartFile


由于某种原因,当我将byte[]作为文件参数传递给upload()方法时,Cloudinary会返回RuntimeException: Missing required parameter - file。这太烦人了。 - moze

14

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关于File.deleteOnExit()在JVM退出时执行(这可能极为罕见)的评论,详情如下。


3
deleteOnExit()方法仅在JVM终止时触发,因此不会在异常发生时触发。因此,在长时间运行的服务器应用程序等应用中使用deleteOnExit()时需要小心。对于服务器应用程序,JVM很少会退出。因此,您需要小心deleteOnExit()导致内存泄漏。 JVM需要跟踪所有需要在退出时删除但未清除的文件,因为JVM不会终止。 - Razzlero
@Razzlero 感谢您指出它只在JVM退出时删除文件。然而它不是内存泄漏,它按设计工作。 - andrej

14
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;
}

1
给出这个异常 java.io.FileNotFoundException: multipdf.pdf (权限被拒绝) - Navnath Adsul

10

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


您可以使用 Apache Commons IO 库和 FileUtils 类。如果您正在使用 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()(通过操作系统)删除所创建的文件创建一个新文件。 - user207421
谢谢您的评论。我检查了方法FileUtils.writeByteArrayToFile的源代码(版本2.4)。我认为,如果文件已经存在,该方法不会重新创建文件。multipartFile对象包含我们想要存储在文件系统中某个位置的上传文件的字节。我的目的是将这些字节存储到首选位置。我保留FileUtils.touch方法的唯一原因是为了明确这是一个新文件。如果文件不存在,FileUtils.writeByteArrayToFile会创建文件(和完整路径),因此不需要FileUtils.touch。 - Georgios Syngouroglou

4

如果接口 MultipartFile 的类是 CommonsMultipartFile,那么您可以通过强制类型转换来在Spring中访问tempfile。

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字节文件的问题,可以在@Configuration @EnableWebMvc类中将maxInMemorySize属性设置为0。之后,所有上传的文件都将存储在磁盘中,而非内存中。
@Bean(name = "multipartResolver")
    public CommonsMultipartResolver createMultipartResolver() {
        CommonsMultipartResolver resolver = new CommonsMultipartResolver();
        resolver.setDefaultEncoding("utf-8");
        resolver.setMaxInMemorySize(0);
        return resolver;
    }

2
在这里,createNewFile() 是毫无意义且浪费的。现在你正在强制 new FileOutputStream()(通过操作系统)删除所创建的文件并创建一个新文件。 - user207421
@EJP 是的,这是毫无意义的,现在我已经修复了编辑时犯下的错误。但是createNewFile()并不浪费资源,因为如果CommonsMultipartFile小于10240字节,则文件系统中不会创建文件。因此,应该在文件系统中创建一个具有任何唯一名称的新文件(我使用了DiskFileItem的名称)。 - Alex78191
@Alex78191 你所说的隐式保存小文件(默认情况下<10240字节)是什么意思?有没有办法增加限制? - Anand Tagore
@AnandTagore 我的意思是,小于10240字节的MultipartFile不会保存在文件系统中,因此需要手动创建Files。 - Alex78191

1
使用Apache Commons进行单行回答。 FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), file); 将multipart文件的输入流复制到文件中。

0
如果你不想使用 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)
    }

0

MultipartFile 可以获取 InputStream。

multipartFile.getInputStream()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接