113

Can any one tell me what is a the best way to convert a multipart file (org.springframework.web.multipart.MultipartFile) to File (java.io.File) ?

In my spring mvc web project i'm getting uploaded file as Multipart file.I have to convert it to a File(io) ,there fore I can call this image storing service(Cloudinary).They only take type (File).

I have done so many searches but failed.If anybody knows a good standard way please let me know? Thnx

Amila Iddamalgoda
  • 4,166
  • 11
  • 46
  • 85

11 Answers11

206

You can get the content of a MultipartFile by using the getBytes method and you can write to the file using 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());
    }
}

You can also use the transferTo method:

public void multipartFileToFile(
    MultipartFile multipart, 
    Path dir
) throws IOException {
    Path filepath = Paths.get(dir.toString(), multipart.getOriginalFilename());
    multipart.transferTo(filepath);
}
Bohemian
  • 412,405
  • 93
  • 575
  • 722
Petros Tsialiamanis
  • 2,718
  • 2
  • 23
  • 35
  • 8
    I used transferTo Function but I feel there is an issue. like It keeps temp file to drive for local machine. – Morez Jun 15 '15 at 14:57
  • @Ronnie I am having the same issue. Have you found any workaround? – Half Blood Prince Apr 19 '16 at 12:22
  • 1
    org.apache.commons.io.FileUtils.deleteQuietly(convFile.getParentFile()); , this should delete the temp file @Ronnie – kavinder Apr 29 '16 at 10:45
  • 5
    `createNewFIle()` is both pointless and wasteful here. You are now obliging `new FileOutputStream()` (via the OS) to both delete the file so created *and* create a new one. – user207421 Sep 06 '16 at 01:14
  • @Petros Tsialiamanis Does it have any File conversion size limit in Java. Let's say I am using 3GB of file. – Rohit May 10 '18 at 12:39
  • what will be the value in `dir` variable ? Seems that it is not possible to create a file object without having any file in local storage. Am I right ? – Rakesh L Sep 11 '19 at 17:37
24

small correction on @PetrosTsialiamanis post , new File( multipart.getOriginalFilename()) this will create file in server location where sometime you will face write permission issues for the user, its not always possible to give write permission to every user who perform action. System.getProperty("java.io.tmpdir") will create temp directory where your file will be created properly. This way you are creating temp folder, where file gets created , later on you can delete file or temp folder.

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

put this method in ur common utility and use it like for eg. Utility.multipartToFile(...)

Swadeshi
  • 1,596
  • 21
  • 33
21

Although the accepted answer is correct but if you are just trying to upload your image to cloudinary, there's a better way:

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

Where multipartFile is your org.springframework.web.multipart.MultipartFile.

Heisenberg
  • 5,514
  • 2
  • 32
  • 43
  • For some reason cloudinary returns `RuntimeException: Missing required parameter - file` when I give the `upload()` method `byte[]` as file param. So annoying. – moze Jul 31 '22 at 12:53
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;
}
ℛɑƒæĿᴿᴹᴿ
  • 4,983
  • 4
  • 38
  • 58
13

MultipartFile.transferTo(File) is nice, but don't forget to clean the temp file after all.

// 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();

Check out Razzlero's comment about File.deleteOnExit() executed upon JVM exit (which may be extremely rare) details below.

andrej
  • 4,518
  • 2
  • 39
  • 39
  • 3
    `deleteOnExit()`, it will only trigger when the JVM terminates, so it won't trigger during exceptions. Because of this you need to be careful using `deleteOnExit()` on long-running applications such as server applications. For server applications the JVM will rarely exit. So you need to be careful of `deleteOnExit()` causing memory leaks. The JVM needs to keep track of all the files which it needs to delete on exit which aren't cleared because the JVM does not terminate. – Razzlero Sep 02 '19 at 07:39
  • @Razzlero thanks for pointing out that it delete files only upon JVM exit. However it's not memory leak, it works as designed. – andrej Sep 03 '19 at 08:47
10

You can also use the Apache Commons IO library and the FileUtils class. In case you are using maven you can load it using the above dependency.

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

The source for the MultipartFile save to disk.

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());
Georgios Syngouroglou
  • 18,813
  • 9
  • 90
  • 92
  • `FileUtils.touch()` is both pointless and wasteful here. You are now obliging `new FileOutputStream()` (via the OS) to both delete the file so created *and* create a new one. – user207421 Sep 06 '16 at 01:16
  • Thank you for your comment. I checked the source of the method FileUtils.writeByteArrayToFile. I think that this method is not re-creating the file in case it exists (version 2.4). The multipartFile object includes the bytes of the uploaded file that we want to store somewhere in the filesystem. My purpose is to store this bytes to a preferred location. The only reason I keep the FileUtils.touch method is to make clear that this is a new file. The FileUtils.writeByteArrayToFile creates the file (and the full path) in case it doesn't exist so the FileUtils.touch is not required. – Georgios Syngouroglou Sep 12 '16 at 10:10
4

You can access tempfile in Spring by casting if the class of interface MultipartFile is CommonsMultipartFile.

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

To get rid of the trick with files less than 10240 bytes maxInMemorySize property can be set to 0 in @Configuration @EnableWebMvc class. After that, all uploaded files will be stored on disk.

@Bean(name = "multipartResolver")
    public CommonsMultipartResolver createMultipartResolver() {
        CommonsMultipartResolver resolver = new CommonsMultipartResolver();
        resolver.setDefaultEncoding("utf-8");
        resolver.setMaxInMemorySize(0);
        return resolver;
    }
Alex78191
  • 2,383
  • 2
  • 17
  • 24
  • 2
    `createNewFIle()` is both pointless and wasteful here. You are now obliging `new FileOutputStream()` (via the OS) to both delete the file so created *and* create a new one. – user207421 Sep 06 '16 at 01:15
  • @EJP yes, it was pointless, now i fix this mistake made while editing. But createNewFIle() is not wasteful, because if CommonsMultipartFile is less than 10240 bytes, the file in filesystem is not created. So a new file with any unique name (i used the name of DiskFileItem) should be created in the FS. – Alex78191 Sep 12 '16 at 23:23
  • @Alex78191 What you mean by implicitly save on disk small files (<10240 bytes by default). Is there anyway to increase the limit – Anand Tagore Sep 29 '16 at 14:14
  • @AnandTagore I mean that MultipartFile's less than 10240 bytes isn't saving in the file system, so Files should be created manually. – Alex78191 Sep 29 '16 at 15:55
1

Single line answer using Apache Commons.

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

Jill
  • 163
  • 1
  • 5
  • 20
0

The answer by Alex78191 has worked for me.

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

For uploading files having size greater than 10240 bytes please change the maxInMemorySize in multipartResolver to 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>
Anand Tagore
  • 672
  • 8
  • 19
  • `maxInMemorySize` has nothing to do with the limitation on file upload size. File upload size is set by the `maxUploadSize` property. – Alex78191 Feb 23 '17 at 05:03
  • 1
    To get rid of the trick with files less than 10240 bytes `maxInMemorySize` prop can be set to `0`. – Alex78191 Feb 23 '17 at 05:04
  • @Alex78191 I have changed this and it worked for me. I had used your code for converting the file. So I changed the properties in the applicationcontext.xml to get rid of the memory limitations. And it works !!! – Anand Tagore Apr 09 '17 at 17:41
  • While creating a file from multipart file, it should be kept in memory. So for that I have to increase the maxInMemorySize. – Anand Tagore May 23 '18 at 07:11
0

if you don't want to use MultipartFile.transferTo(). You can write file like this

    val dir = File(filePackagePath)
    if (!dir.exists()) dir.mkdirs()

    val file = File("$filePackagePath${multipartFile.originalFilename}").apply {
        createNewFile()
    }

    FileOutputStream(file).use {
        it.write(multipartFile.bytes)
    }
Artem Botnev
  • 2,267
  • 1
  • 14
  • 19
0

MultipartFile can get InputStream.

multipartFile.getInputStream()

anson
  • 1,436
  • 14
  • 16