Jak przekonwertować plik wieloczęściowy do pliku?

90

Czy ktoś może mi powiedzieć, jaki jest najlepszy sposób konwersji pliku wieloczęściowego (org.springframework.web.multipart.MultipartFile) do pliku (java.io.File)?

W moim wiosennym projekcie internetowym mvc otrzymuję przesyłany plik jako plik Multipart.Muszę przekonwertować go do pliku (io), dlatego mogę wywołać tę usługę przechowywania obrazów ( Cloudinary ) .Biorą one tylko typ (plik).

Zrobiłem tyle wyszukiwań, ale nie udało mi się. Jeśli ktoś zna dobry standardowy sposób, daj mi znać? Thnx

Amila Iddamalgoda
źródło
5
Czy jest coś, co uniemożliwia Ci skorzystanie z tej metody MultipartFile.transferTo()?
fajarkoe

Odpowiedzi:

192

Możesz pobrać zawartość a MultipartFileużywając getBytesmetody i możesz pisać do pliku za pomocą 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());
    }
}

Możesz również skorzystać z metody transferTo :

public void multipartFileToFile(
    MultipartFile multipart, 
    Path dir
) throws IOException {
    Path filepath = Paths.get(dir.toString(), multipart.getOriginalFilename());
    multipart.transferTo(filepath);
}
Petros Tsialiamanis
źródło
7
Użyłem funkcji TransferTo, ale czuję, że jest problem. jak Przechowuje plik tymczasowy do napędu dla lokalnego komputera.
Morez
@Ronnie Mam ten sam problem. Czy znalazłeś jakieś obejście?
Half Blood Prince
1
org.apache.commons.io.FileUtils.deleteQuietly (convFile.getParentFile ()); , powinno to usunąć plik tymczasowy @Ronnie
kavinder
5
createNewFIle()jest tu bezcelowy i marnotrawny. Teraz zobowiązujesz się new FileOutputStream()(przez system operacyjny) zarówno do usunięcia tak utworzonego pliku, jak i do utworzenia nowego.
Markiz Lorne
@Petros Tsialiamanis Czy ma limit rozmiaru konwersji plików w Javie. Powiedzmy, że używam 3 GB pliku.
Rohit
17

Chociaż zaakceptowana odpowiedź jest prawidłowa, ale jeśli próbujesz tylko przesłać swój obraz do Cloudinary, jest lepszy sposób:

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

Gdzie multipartFile to Twój plik org.springframework.web.multipart.MultipartFile .

Heisenberg
źródło
17

mała poprawka w poście @PetrosTsialiamanis, new File( multipart.getOriginalFilename())spowoduje to utworzenie pliku w lokalizacji serwera, gdzie czasami napotkasz problemy z uprawnieniami do zapisu dla użytkownika, nie zawsze jest możliwe nadanie uprawnień do zapisu każdemu użytkownikowi, który wykonuje akcję. System.getProperty("java.io.tmpdir")utworzy katalog tymczasowy, w którym plik zostanie utworzony poprawnie. W ten sposób tworzysz folder tymczasowy, w którym zostanie utworzony plik, później możesz usunąć plik lub folder tymczasowy.

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

umieść tę metodę w powszechnym narzędziu i używaj jej jak np. Utility.multipartToFile(...)

Swadeshi
źródło
8

Możesz także skorzystać z biblioteki Apache Commons IO i klasy FileUtils . Jeśli używasz mavena, możesz go załadować, korzystając z powyższej zależności.

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

Źródło zapisu MultipartFile na dysku.

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
źródło
FileUtils.touch()jest tu bezcelowy i marnotrawny. Teraz zobowiązujesz się new FileOutputStream()(przez system operacyjny) zarówno do usunięcia tak utworzonego pliku, jak i do utworzenia nowego.
Markiz Lorne
Dziękuję za Twój komentarz. Sprawdziłem źródło metody FileUtils.writeByteArrayToFile. Myślę, że ta metoda nie polega na odtworzeniu pliku, jeśli istnieje (wersja 2.4). Obiekt multipartFile zawiera bajty przesłanego pliku, który chcemy przechowywać gdzieś w systemie plików. Moim celem jest przechowywanie tych bajtów w preferowanej lokalizacji. Jedynym powodem, dla którego zachowuję metodę FileUtils.touch, jest wyjaśnienie, że jest to nowy plik. FileUtils.writeByteArrayToFile tworzy plik (i pełną ścieżkę) na wypadek, gdyby nie istniał, więc FileUtils.touch nie jest wymagany.
George Siggouroglou,
7

MultipartFile.transferTo (File) jest fajny, ale nie zapomnij jednak wyczyścić pliku tymczasowego.

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

Sprawdź komentarz Razzlero na temat File.deleteOnExit () wykonywanego przy wyjściu JVM (co może być niezwykle rzadkie) szczegóły poniżej.

andrej
źródło
2
deleteOnExit(), będzie wyzwalane tylko po zakończeniu działania maszyny JVM, więc nie będzie wyzwalane podczas wyjątków. Z tego powodu należy zachować ostrożność podczas korzystania deleteOnExit()z długotrwałych aplikacji, takich jak aplikacje serwerowe. W przypadku aplikacji serwerowych JVM rzadko kończy działanie. Musisz więc uważać, aby nie deleteOnExit()spowodować wycieków pamięci. JVM musi śledzić wszystkie pliki, które musi usunąć przy wyjściu, które nie są czyszczone, ponieważ JVM nie kończy działania.
Razzlero
@Razzlero dzięki za wskazanie, że usuwa pliki tylko po wyjściu z JVM. Jednak nie jest to wyciek pamięci, działa zgodnie z projektem.
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
źródło
dając ten wyjątek java.io.FileNotFoundException: multipdf.pdf (Odmowa uprawnień)
Navnath Adsul
1

Możesz uzyskać dostęp do pliku tymczasowego w Spring przez rzutowanie, jeśli klasa interfejsu MultipartFileto 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;
}

Aby pozbyć się sztuczki z plikami mniejszymi niż 10240 bajtów, maxInMemorySizewłaściwość można ustawić na 0 w @Configuration @EnableWebMvcklasie. Następnie wszystkie przesłane pliki zostaną zapisane na dysku.

@Bean(name = "multipartResolver")
    public CommonsMultipartResolver createMultipartResolver() {
        CommonsMultipartResolver resolver = new CommonsMultipartResolver();
        resolver.setDefaultEncoding("utf-8");
        resolver.setMaxInMemorySize(0);
        return resolver;
    }
Alex78191
źródło
2
createNewFIle()jest tu bezcelowy i marnotrawny. Teraz zobowiązujesz się new FileOutputStream()(przez system operacyjny) zarówno do usunięcia tak utworzonego pliku, jak i do utworzenia nowego.
Markiz Lorne
@EJP tak, bez sensu, teraz naprawiam ten błąd popełniony podczas edycji. Ale createNewFIle () nie jest marnotrawstwem, ponieważ jeśli CommonsMultipartFile ma mniej niż 10240 bajtów, plik w systemie plików nie jest tworzony. Dlatego w FS powinien zostać utworzony nowy plik o dowolnej unikalnej nazwie (użyłem nazwy DiskFileItem).
Alex78191
@ Alex78191 Co masz na myśli przez niejawne zapisywanie na dysku małych plików (domyślnie <10240 bajtów). Czy w ogóle można zwiększyć limit
Anand Tagore
@AnandTagore Mam na myśli to, że mniej niż 10240 bajtów MultipartFile nie zapisuje się w systemie plików, więc pliki powinny być tworzone ręcznie.
Alex78191,
0

Odpowiedź Alex78191 zadziałała dla mnie.

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

Aby przesłać pliki o rozmiarze większym niż 10240 bajtów, zmień maxInMemorySize w multipartResolver na 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
źródło
maxInMemorySizenie ma nic wspólnego z ograniczeniem rozmiaru przesyłanych plików. Rozmiar przesyłanego pliku jest ustawiany przez maxUploadSizeusługę.
Alex78191
Aby pozbyć się tej sztuczki z plikami mniejszymi niż 10240 bajtów, maxInMemorySizemożna ustawić właściwość 0.
Alex78191
@ Alex78191 Zmieniłem to i zadziałało dla mnie. Użyłem twojego kodu do konwersji pliku. Więc zmieniłem właściwości w applicationcontext.xml, aby pozbyć się ograniczeń pamięci. I to działa !!!
Anand Tagore
Podczas tworzenia pliku z pliku wieloczęściowego należy go przechowywać w pamięci. W tym celu muszę zwiększyć maxInMemorySize.
Anand Tagore
0

jeśli nie chcesz używać MultipartFile.transferTo (). Możesz napisać plik w ten sposób

    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
źródło