¿Cómo convertir un archivo multiparte a un archivo?

90

¿Alguien puede decirme cuál es la mejor manera de convertir un archivo de varias partes (org.springframework.web.multipart.MultipartFile) a un archivo (java.io.File)?

En mi proyecto web spring mvc, estoy subiendo un archivo como archivo Multipart. Tengo que convertirlo en un Archivo (io), por lo tanto, puedo llamar a este servicio de almacenamiento de imágenes ( Cloudinary ). Solo toman el tipo (Archivo).

He hecho tantas búsquedas pero he fallado. Si alguien conoce un método estándar bueno, hágamelo saber. Thnx

Amila Iddamalgoda
fuente
5
¿Hay algo que le impida utilizar el método MultipartFile.transferTo()?
fajarkoe

Respuestas:

192

Puede obtener el contenido de a MultipartFileusando el getBytesmétodo y puede escribir en el archivo 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());
    }
}

También puede utilizar el 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
fuente
7
Utilicé transferTo Function pero creo que hay un problema. como Mantiene el archivo temporal para conducir por la máquina local.
Morez
@Ronnie Estoy teniendo el mismo problema. ¿Ha encontrado alguna solución?
Príncipe Mestizo
1
org.apache.commons.io.FileUtils.deleteQuietly (convFile.getParentFile ()); , esto debería eliminar el archivo temporal @Ronnie
kavinder
5
createNewFIle()aquí es inútil y derrochador. Ahora está obligando new FileOutputStream()(a través del sistema operativo) a eliminar el archivo así creado y crear uno nuevo.
Marqués de Lorne
@Petros Tsialiamanis ¿Tiene algún límite de tamaño de conversión de archivos en Java? Digamos que estoy usando 3GB de archivo.
Rohit
17

Aunque la respuesta aceptada es correcta, pero si solo está intentando cargar su imagen en cloudinary, hay una mejor manera:

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

Donde multipartFile es su org.springframework.web.multipart.MultipartFile .

Heisenberg
fuente
17

pequeña corrección en la publicación de @PetrosTsialiamanis, new File( multipart.getOriginalFilename())esto creará un archivo en la ubicación del servidor donde en algún momento enfrentará problemas de permisos de escritura para el usuario, no siempre es posible otorgar permisos de escritura a todos los usuarios que realizan una acción. System.getProperty("java.io.tmpdir")creará un directorio temporal donde su archivo se creará correctamente. De esta manera, está creando una carpeta temporal, donde se crea el archivo, más adelante puede eliminar el archivo o la carpeta temporal.

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

ponga este método en su utilidad común y utilícelo como por ejemplo. Utility.multipartToFile(...)

Swadeshi
fuente
8

También puede utilizar la biblioteca IO de Apache Commons y la clase FileUtils . En caso de que esté utilizando maven, puede cargarlo utilizando la dependencia anterior.

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

La fuente para guardar MultipartFile en 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
fuente
FileUtils.touch()aquí es inútil y derrochador. Ahora está obligando new FileOutputStream()(a través del sistema operativo) a eliminar el archivo así creado y crear uno nuevo.
Marqués de Lorne
Gracias por tu comentario. Verifiqué la fuente del método FileUtils.writeByteArrayToFile. Creo que este método no vuelve a crear el archivo en caso de que exista (versión 2.4). El objeto multipartFile incluye los bytes del archivo cargado que queremos almacenar en algún lugar del sistema de archivos. Mi propósito es almacenar estos bytes en una ubicación preferida. La única razón por la que conservo el método FileUtils.touch es para dejar en claro que se trata de un archivo nuevo. FileUtils.writeByteArrayToFile crea el archivo (y la ruta completa) en caso de que no exista, por lo que FileUtils.touch no es necesario.
George Siggouroglou
7

MultipartFile.transferTo (File) es bueno, pero no olvides limpiar el archivo temporal después de todo.

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

Consulte el comentario de Razzlero sobre File.deleteOnSalir () ejecutado al salir de JVM (que puede ser extremadamente raro) detalles a continuación.

andrej
fuente
2
deleteOnExit(), solo se activará cuando la JVM finalice, por lo que no se activará durante las excepciones. Debido a esto, debe tener cuidado al usar aplicaciones deleteOnExit()de larga ejecución, como aplicaciones de servidor. Para aplicaciones de servidor, la JVM rara vez se cerrará. Por lo tanto, debe tener cuidado de no deleteOnExit()causar pérdidas de memoria. La JVM necesita realizar un seguimiento de todos los archivos que necesita eliminar al salir y que no se borran porque la JVM no termina.
Razzlero
@Razzlero gracias por señalar que elimina archivos solo al salir de JVM. Sin embargo, no se trata de una fuga de memoria, funciona según lo diseñado.
andrej
5
  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
fuente
dando esta excepción java.io.FileNotFoundException: multipdf.pdf (Permiso denegado)
Navnath Adsul
1

Puede acceder al archivo temporal en Spring lanzando si la clase de interfaz MultipartFilees 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 deshacerse del truco con archivos de menos de 10240 bytes, la maxInMemorySizepropiedad se puede establecer en 0 en la @Configuration @EnableWebMvcclase. Después de eso, todos los archivos cargados se almacenarán en el disco.

@Bean(name = "multipartResolver")
    public CommonsMultipartResolver createMultipartResolver() {
        CommonsMultipartResolver resolver = new CommonsMultipartResolver();
        resolver.setDefaultEncoding("utf-8");
        resolver.setMaxInMemorySize(0);
        return resolver;
    }
Alex78191
fuente
2
createNewFIle()aquí es inútil y derrochador. Ahora está obligando new FileOutputStream()(a través del sistema operativo) a eliminar el archivo así creado y crear uno nuevo.
Marqués de Lorne
@EJP sí, no tenía sentido, ahora soluciono este error cometido durante la edición. Pero createNewFIle () no es un desperdicio, porque si CommonsMultipartFile tiene menos de 10240 bytes, el archivo en el sistema de archivos no se crea. Entonces, se debe crear un nuevo archivo con cualquier nombre único (utilicé el nombre de DiskFileItem) en el FS.
Alex78191
@ Alex78191 ¿Qué quiere decir con guardar implícitamente en el disco archivos pequeños (<10240 bytes por defecto). ¿Hay alguna forma de aumentar el límite
Anand Tagore
@AnandTagore Me refiero a que los menos de 10240 bytes de MultipartFile no se están guardando en el sistema de archivos, por lo que los archivos deben crearse manualmente.
Alex78191
0

La respuesta de Alex78191 me ha funcionado.

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 cargar archivos con un tamaño superior a 10240 bytes, cambie maxInMemorySize en multipartResolver a 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
fuente
maxInMemorySizeno tiene nada que ver con la limitación del tamaño de carga del archivo. El tamaño de carga del archivo lo establece la maxUploadSizepropiedad.
Alex78191
Para deshacerse del truco con archivos de menos de 10240 bytes maxInMemorySize, se puede configurar prop 0.
Alex78191
@ Alex78191 He cambiado esto y funcionó para mí. Usé tu código para convertir el archivo. Así que cambié las propiedades en applicationcontext.xml para deshacerme de las limitaciones de memoria. Y funciona !!!
Anand Tagore
Al crear un archivo a partir de un archivo de varias partes, debe mantenerse en la memoria. Entonces, para eso tengo que aumentar maxInMemorySize.
Anand Tagore
0

si no desea utilizar MultipartFile.transferTo (). Puedes escribir un archivo 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
fuente