Como converter um arquivo multiparte em arquivo?

88

Alguém pode me dizer qual é a melhor maneira de converter um arquivo multiparte (org.springframework.web.multipart.MultipartFile) em arquivo (java.io.File)?

No meu projeto da web do Spring mvc, estou recebendo um arquivo carregado como um arquivo multipart. Tenho que convertê-lo em um arquivo (io), portanto, posso chamar esse serviço de armazenamento de imagens ( Cloudinary ). Eles aceitam apenas o tipo (arquivo).

Fiz muitas pesquisas, mas falhei. Se alguém souber de uma boa maneira padrão, por favor me avise. Thnx

Amila Iddamalgoda
fonte
5
Existe algo que o impede de usar o método MultipartFile.transferTo()?
fajarkoe

Respostas:

192

Você pode obter o conteúdo de um MultipartFileusando o getBytesmétodo e pode gravar no arquivo usando 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());
    }
}

Você também pode usar o método transferTo :

public void multipartFileToFile(
    MultipartFile multipart, 
    Path dir
) throws IOException {
    Path filepath = Paths.get(dir.toString(), multipart.getOriginalFilename());
    multipart.transferTo(filepath);
}
Petros Tsialiamanis
fonte
7
Usei a função transferTo, mas sinto que há um problema. como mantém o arquivo temporário para dirigir para a máquina local.
Morez de
@Ronnie Estou tendo o mesmo problema. Você encontrou alguma solução alternativa?
Enigma do Príncipe de
1
org.apache.commons.io.FileUtils.deleteQuietly (convFile.getParentFile ()); , isso deve excluir o arquivo temporário @Ronnie
kavinder
5
createNewFIle()é inútil e um desperdício aqui. Agora você está obrigando new FileOutputStream()(por meio do sistema operacional) a excluir o arquivo criado e a criar um novo.
Marquês de Lorne
@Petros Tsialiamanis Tem algum limite de tamanho de conversão de arquivo em Java. Digamos que estou usando 3 GB de arquivo.
Rohit de
16

Embora a resposta aceita esteja correta, mas se você está apenas tentando enviar sua imagem para o cloudinary, existe uma maneira melhor:

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

Onde multipartFile é seu org.springframework.web.multipart.MultipartFile .

Heisenberg
fonte
16

pequena correção na postagem de @PetrosTsialiamanis, new File( multipart.getOriginalFilename())isso criará um arquivo no local do servidor onde em algum momento você enfrentará problemas de permissão de gravação para o usuário, nem sempre é possível dar permissão de gravação a todos os usuários que executam a ação. System.getProperty("java.io.tmpdir")irá criar um diretório temporário onde seu arquivo será criado corretamente. Desta forma, você está criando uma pasta temporária, onde o arquivo é criado, mais tarde você pode excluir o arquivo ou pasta temporária.

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

coloque este método em seu utilitário comum e use-o como por exemplo. Utility.multipartToFile(...)

Swadeshi
fonte
8

Você também pode usar a biblioteca Apache Commons IO e a classe FileUtils . Caso você esteja usando o maven, você pode carregá-lo usando a dependência acima.

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

A fonte para salvar MultipartFile no disco.

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());
George Siggouroglou
fonte
FileUtils.touch()é inútil e um desperdício aqui. Agora você está obrigando new FileOutputStream()(por meio do sistema operacional) a excluir o arquivo criado e a criar um novo.
Marquês de Lorne
Obrigado pelo seu comentário. Verifiquei a fonte do método FileUtils.writeByteArrayToFile. Acho que esse método não está recriando o arquivo caso ele exista (versão 2.4). O objeto multipartFile inclui os bytes do arquivo carregado que queremos armazenar em algum lugar do sistema de arquivos. Meu objetivo é armazenar esses bytes em um local preferido. O único motivo pelo qual mantenho o método FileUtils.touch é para deixar claro que este é um novo arquivo. O FileUtils.writeByteArrayToFile cria o arquivo (e o caminho completo) caso ele não exista, então o FileUtils.touch não é necessário.
George Siggouroglou
7

MultipartFile.transferTo (File) é bom, mas não se esqueça de limpar o arquivo temporário depois de tudo.

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

Confira o comentário de Razzlero sobre File.deleteOnExit () executado na saída da JVM (que pode ser extremamente rara), os detalhes abaixo.

andrej
fonte
2
deleteOnExit(), ele só será acionado quando a JVM for encerrada, portanto, não será acionado durante as exceções. Por isso, você precisa ter cuidado ao usar deleteOnExit()aplicativos de longa duração, como aplicativos de servidor. Para aplicativos de servidor, a JVM raramente será encerrada. Portanto, você precisa ter cuidado para não deleteOnExit()causar vazamentos de memória. A JVM precisa manter o controle de todos os arquivos que precisa excluir na saída e que não foram limpos porque a JVM não é encerrada.
Razzlero
@Razzlero agradece por apontar que ele exclui arquivos apenas após a saída da JVM. No entanto, não é vazamento de memória, ele funciona conforme o planejado.
andrej
4
  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;
    }
sachintha hewawasam
fonte
dando esta exceção java.io.FileNotFoundException: multipdf.pdf (permissão negada)
Navnath Adsul
1

Você pode acessar o arquivo temporário no Spring lançando se a classe da interface MultipartFilefor 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;
}

Para se livrar do truque com arquivos com menos de 10240 bytes, a maxInMemorySizepropriedade pode ser definida como 0 na @Configuration @EnableWebMvcclasse. Depois disso, todos os arquivos carregados serão armazenados no disco.

@Bean(name = "multipartResolver")
    public CommonsMultipartResolver createMultipartResolver() {
        CommonsMultipartResolver resolver = new CommonsMultipartResolver();
        resolver.setDefaultEncoding("utf-8");
        resolver.setMaxInMemorySize(0);
        return resolver;
    }
Alex78191
fonte
2
createNewFIle()é inútil e um desperdício aqui. Agora você está obrigando new FileOutputStream()(por meio do sistema operacional) a excluir o arquivo criado e a criar um novo.
Marquês de Lorne
@EJP sim, não adiantou, agora eu conserto esse erro cometido durante a edição. Mas createNewFIle () não é um desperdício, porque se CommonsMultipartFile tiver menos de 10240 bytes, o arquivo no sistema de arquivos não será criado. Portanto, um novo arquivo com qualquer nome exclusivo (usei o nome de DiskFileItem) deve ser criado no FS.
Alex78191
@ Alex78191 O que você quer dizer com salvar implicitamente no disco pequenos arquivos (<10240 bytes por padrão). Existe alguma maneira de aumentar o limite
Anand Tagore
@AnandTagore Quero dizer que menos de 10240 bytes de MultipartFile não está salvando no sistema de arquivos, então os arquivos devem ser criados manualmente.
Alex78191
0

A resposta de Alex78191 funcionou para mim.

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

Para fazer upload de arquivos com tamanho superior a 10240 bytes, altere maxInMemorySize em multipartResolver para 1 MB.

<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
fonte
maxInMemorySizenão tem nada a ver com a limitação do tamanho de upload do arquivo. O tamanho do upload do arquivo é definido pela maxUploadSizepropriedade.
Alex78191
Para se livrar do truque com arquivos com menos de 10240 bytes, maxInMemorySizeprop pode ser definido como 0.
Alex78191
@ Alex78191 Eu mudei isso e funcionou para mim. Usei seu código para converter o arquivo. Portanto, alterei as propriedades no applicationcontext.xml para me livrar das limitações de memória. E funciona !!!
Anand Tagore,
Ao criar um arquivo a partir de um arquivo multiparte, ele deve ser mantido na memória. Portanto, para isso tenho que aumentar o maxInMemorySize.
Anand Tagore
0

se você não quiser usar MultipartFile.transferTo (). Você pode escrever um arquivo como este

    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
fonte