วิธีการแปลงไฟล์มัลติพาร์ทเป็นไฟล์


92

ใครช่วยบอกทีว่าวิธีที่ดีที่สุดในการแปลงไฟล์มัลติพาร์ท (org.springframework.web.multipart.MultipartFile) เป็นไฟล์ (java.io.File) คืออะไร

ในโครงการเว็บ mvc ฤดูใบไม้ผลิของฉันฉันได้รับการอัปโหลดไฟล์เป็นไฟล์ Multipart ฉันต้องแปลงเป็นไฟล์ (io) ซึ่งก่อนหน้านี้ฉันสามารถเรียกบริการจัดเก็บรูปภาพนี้ ( Cloudinary ) ได้โดยใช้ประเภท (File) เท่านั้น

ฉันทำการค้นหาหลายครั้ง แต่ล้มเหลวหากใครรู้วิธีมาตรฐานที่ดีโปรดแจ้งให้เราทราบ? Thnx


5
มีบางอย่างที่ขัดขวางไม่ให้คุณใช้วิธีนี้MultipartFile.transferTo()หรือไม่?
fajarkoe

คำตอบ:


195

คุณสามารถรับเนื้อหาของ a MultipartFileโดยใช้getBytesวิธีการและคุณสามารถเขียนลงไฟล์โดยใช้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);
}

7
ฉันใช้ฟังก์ชัน transferTo แต่รู้สึกว่ามีปัญหา เช่นเก็บไฟล์ temp เพื่อขับเคลื่อนสำหรับเครื่องท้องถิ่น
Morez

@Ronnie ฉันมีปัญหาเดียวกัน คุณพบวิธีแก้ปัญหาหรือไม่?
Half Blood Prince

1
org.apache.commons.io.FileUtils.deleteQuietly (convFile.getParentFile ()); มันควรจะลบไฟล์ temp @Ronnie
kavinder

5
createNewFIle()ทั้งไร้จุดหมายและสิ้นเปลืองที่นี่ ขณะนี้คุณมีnew FileOutputStream()ภาระผูกพัน(ผ่านระบบปฏิบัติการ) ในการลบไฟล์ทั้งสองเพื่อสร้างและสร้างไฟล์ใหม่
user207421

@Petros Tsialiamanis มีขีด จำกัด ขนาดการแปลงไฟล์ใน Java หรือไม่ สมมติว่าฉันใช้ไฟล์ 3GB
Rohit

19

การแก้ไขเล็กน้อยในโพสต์ @PetrosTsialiamanis new File( multipart.getOriginalFilename())สิ่งนี้จะสร้างไฟล์ในตำแหน่งเซิร์ฟเวอร์ซึ่งบางครั้งคุณจะประสบปัญหาสิทธิ์การเขียนสำหรับผู้ใช้ซึ่งเป็นไปไม่ได้ที่จะให้สิทธิ์ในการเขียนแก่ผู้ใช้ทุกคนที่ดำเนินการ System.getProperty("java.io.tmpdir")จะสร้างไดเร็กทอรี temp ที่ไฟล์ของคุณจะถูกสร้างขึ้นอย่างถูกต้อง ด้วยวิธีนี้คุณกำลังสร้างโฟลเดอร์ชั่วคราวซึ่งจะสร้างไฟล์ขึ้นมาในภายหลังคุณสามารถลบไฟล์หรือโฟลเดอร์ชั่วคราวได้

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


นี่น่าจะเป็นคำตอบที่ถูกต้อง (โดยเฉพาะอย่างยิ่งสำหรับการใช้ไดเรกทอรี tmp เพราะมันจะแก้ปัญหาบางอย่างเช่นการอนุญาตถูกปฏิเสธใน linux)
Houssem Badri

17

แม้ว่าคำตอบที่ยอมรับจะถูกต้อง แต่หากคุณแค่พยายามอัปโหลดภาพไปยังระบบคลาวด์ก็มีวิธีที่ดีกว่า:

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

ที่ไหน multipartFile เป็นของคุณorg.springframework.web.multipart.MultipartFile


8

นอกจากนี้คุณยังสามารถใช้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
George Siggouroglou

7

MultipartFile.transferTo (File) ก็ดี แต่อย่าลืมล้างไฟล์ temp ด้วย

// 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 (ซึ่งอาจหายากมาก) รายละเอียดด้านล่าง


2
deleteOnExit()มันจะทริกเกอร์เมื่อ JVM ยุติเท่านั้นดังนั้นจึงไม่ทริกเกอร์ในระหว่างข้อยกเว้น ด้วยเหตุนี้คุณจึงต้องระมัดระวังdeleteOnExit()ในการใช้งานแอปพลิเคชันที่ใช้งานมานานเช่นแอปพลิเคชันเซิร์ฟเวอร์ สำหรับแอปพลิเคชันเซิร์ฟเวอร์ JVM จะไม่ค่อยออก ดังนั้นคุณต้องระวังการdeleteOnExit()รั่วไหลของหน่วยความจำ JVM จำเป็นต้องติดตามไฟล์ทั้งหมดที่ต้องการลบเมื่อออกซึ่งไม่ได้ล้างเนื่องจาก JVM ไม่ยุติ
Razzlero

@Razzlero ขอบคุณที่ชี้ให้เห็นว่าจะลบไฟล์เมื่อออกจาก JVM เท่านั้น อย่างไรก็ตามมันไม่ใช่หน่วยความจำรั่ว แต่ทำงานได้ตามที่ออกแบบไว้
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;
    }

ให้ข้อยกเว้นนี้ java.io.FileNotFoundException: multipdf.pdf (ปฏิเสธการอนุญาต)
Navnath Adsul

2

คุณสามารถเข้าถึง tempfile ในฤดูใบไม้ผลิโดยการหล่อถ้าระดับของอินเตอร์เฟซเป็นMultipartFileCommonsMultipartFile

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

ในการกำจัดเคล็ดลับด้วยไฟล์ที่มีmaxInMemorySizeคุณสมบัติน้อยกว่า 10240 ไบต์สามารถตั้งค่าเป็น 0 ใน@Configuration @EnableWebMvcคลาสได้ หลังจากนั้นไฟล์ที่อัปโหลดทั้งหมดจะถูกเก็บไว้ในดิสก์

@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) ควรถูกสร้างใน FS
Alex78191

@ Alex78191 คุณหมายถึงอะไรโดยปริยายบันทึกบนดิสก์ไฟล์ขนาดเล็ก (<10240 ไบต์โดยค่าเริ่มต้น) จะมีการเพิ่มขีด จำกัด หรือไม่
Anand Tagore

@AnandTagore ฉันหมายความว่า MultipartFile ที่น้อยกว่า 10240 ไบต์จะไม่ถูกบันทึกในระบบไฟล์ดังนั้นควรสร้างไฟล์ด้วยตนเอง
Alex78191

0

คำตอบของ Alex78191 ได้ผลสำหรับฉัน

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 ไบต์โปรดเปลี่ยน maxInMemorySize ใน multipartResolver เป็น 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>

maxInMemorySizeไม่มีส่วนเกี่ยวข้องกับข้อ จำกัด ของขนาดการอัปโหลดไฟล์ ขนาดไฟล์อัพโหลดถูกกำหนดโดยmaxUploadSizeคุณสมบัติ
Alex78191

1
หากต้องการกำจัดเคล็ดลับที่มีไฟล์น้อยกว่า 10240 ไบต์maxInMemorySizeสามารถตั้งค่าเป็น0ไฟล์.
Alex78191

@ Alex78191 ฉันเปลี่ยนสิ่งนี้แล้วและมันก็ได้ผลสำหรับฉัน ฉันใช้รหัสของคุณในการแปลงไฟล์ ดังนั้นฉันจึงเปลี่ยนคุณสมบัติใน applicationcontext.xml เพื่อกำจัดข้อ จำกัด ของหน่วยความจำ และได้ผล !!!
Anand Tagore

ในขณะที่สร้างไฟล์จากไฟล์หลายส่วนควรเก็บไว้ในหน่วยความจำ ดังนั้นฉันจึงต้องเพิ่ม maxInMemorySize
Anand Tagore

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)
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.