Java : InputStream to Multi-part file conversion, result file is empty

I am working on a Java application in which I am trying to create a Multipart file out of downloaded InputStream. Unfortunately, it is not working and the Multipart file is empty. I checked the size of savedFile on disk before copying it to Multipart, and it has correct size, attributes, content.

What am I doing wrong in the conversion, there is no stacktrace, as I am catching it.

Code :

// InputStream contains file data. byte[] bytes = IOUtils.toByteArray(inputStream); File file = new File(msg + "temp"); if (file.exists() && file.isDirectory()) { OutputStream outputStream = new FileOutputStream(new File(msg + "temp" + "/" + groupAttachments.getFileName())); outputStream.write(bytes); outputStream.close(); } java.io.File savedFile = new java.io.File(msg + "temp" + "/" + groupAttachments.getFileName()); DiskFileItem fileItem = new DiskFileItem("file", "text/plain", false, savedFile.getName(), (int) savedFile.length(), savedFile.getParentFile()); fileItem.getOutputStream(); MultipartFile multipartFile = new CommonsMultipartFile(fileItem); System.out.println("Saved file size is "+savedFile.length()); if (multipartFile.isEmpty()) { System.out.println("Dropbox uploaded multipart file is empty"); } else { System.out.println("Multipart file is not empty."); } this.dropboxTask.insertFile(multipartFile, "", savedPersonalNoteObject.getNoteid(), (long) 0, true); Path path = Paths.get(msg + "temp" + "/" + groupAttachments.getFileName()); 

Console output :

Multipart file is not empty Bytes are not null File path is /My Group Input stream is not null Saved file size is 4765 Dropbox uploaded multipart file is empty Multipart file is empty Bytes are not null 

What am I doing wrong in the conversion? Any help would be nice. Thanks a lot.

0

1 Answer

The DiskFileItem uses a DeferredFileOutputStream which uses an in-memory byte-array that is only filled when bytes are actually transferred. Since files are used directly and no bytes are actually copied, the byte-array is never filled. See for yourself in the source code:
Source code CommonsMultipartFile
Source code DiskFileItem
Source code DeferredFileOutputStream

So, instead of just calling fileItem.getOutputStream();, transfer the bytes to fill the in-memory byte-array:

try (OutputStream out = fileItem.getOutputStream(); InputStream in = Files.newInputStream(file.toPath())) { IOUtils.copy(in, dfos); } 

and then the tranferTo call will work.
This appears to be a bit cumbersome for just moving a file: CommonsMultipartFile only calls fileItem.write((File)dest) in the transferTo method. Below are two test cases, one using the DiskFileItem and one using the LocalFileItem. The code for LocalFileItem is shown further below.
I used dependencies org.springframework:spring-web:4.2.2.RELEASE, commons-fileupload:commons-fileupload:1.3.1 and junit:junit:4.12
Test class CommonMp:

import static org.junit.Assert.*; import java.io.*; import java.nio.charset.*; import java.nio.file.*; import org.apache.commons.fileupload.disk.DiskFileItem; import org.apache.commons.io.IOUtils; import org.junit.Test; import org.springframework.web.multipart.commons.CommonsMultipartFile; public class CommonMp { private final Charset CS = StandardCharsets.UTF_8; @Test public void testLocalMp() { Path testInputFile = null, testOutputFile = null; try { testInputFile = prepareInputFile(); LocalFileItem lfi = new LocalFileItem(testInputFile); CommonsMultipartFile cmf = new CommonsMultipartFile(lfi); System.out.println("Empty: " + cmf.isEmpty()); testOutputFile = testInputFile.getParent().resolve("testMpOutput.txt"); cmf.transferTo(testOutputFile.toFile()); System.out.println("Size: " + cmf.getSize()); printOutput(testOutputFile); } catch (Exception e) { e.printStackTrace(); fail(); } finally { deleteSilent(testInputFile, testOutputFile); } } @Test public void testMp() { Path testInputFile = null, testOutputFile = null; try { testInputFile = prepareInputFile(); DiskFileItem di = new DiskFileItem("file", "text/plain", false, testInputFile.getFileName().toString(), (int) Files.size(testInputFile), testInputFile.getParent().toFile()); try (OutputStream out = di.getOutputStream(); InputStream in = Files.newInputStream(testInputFile)) { IOUtils.copy(in, out); } CommonsMultipartFile cmf = new CommonsMultipartFile(di); System.out.println("Size: " + cmf.getSize()); testOutputFile = testInputFile.getParent().resolve("testMpOutput.txt"); cmf.transferTo(testOutputFile.toFile()); printOutput(testOutputFile); } catch (Exception e) { e.printStackTrace(); fail(); } finally { deleteSilent(testInputFile, testOutputFile); } } private Path prepareInputFile() throws IOException { Path tmpDir = Paths.get(System.getProperty("java.io.tmpdir")); Path testInputFile = tmpDir.resolve("testMpinput.txt"); try (OutputStream out = Files.newOutputStream(testInputFile)){ out.write("Just a test.".getBytes(CS)); } return testInputFile; } private void printOutput(Path p) throws IOException { byte[] outBytes = Files.readAllBytes(p); System.out.println("Output: " + new String(outBytes, CS)); } private void deleteSilent(Path... paths) { for (Path p : paths) { try { if (p != null) p.toFile().delete(); } catch (Exception ignored) {} } } } 

The custom LocalFileItem class, YMMV!

import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemHeaders; public class LocalFileItem implements FileItem { private static final long serialVersionUID = 2467880290855097332L; private final Path localFile; public LocalFileItem(Path localFile) { this.localFile = localFile; } @Override public void write(File file) throws Exception { Files.move(localFile, file.toPath(), StandardCopyOption.REPLACE_EXISTING); } @Override public long getSize() { // Spring's CommonsMultipartFile caches the file size and uses it to determine availability. long size = -1L; try { size = Files.size(localFile); } catch (IOException ignored) {} return size; } @Override public void delete() { localFile.toFile().delete(); } /* *** properties and unsupported methods *** */ private FileItemHeaders headers; private String contentType; private String fieldName; private boolean formField; @Override public FileItemHeaders getHeaders() { return headers; } @Override public void setHeaders(FileItemHeaders headers) { this.headers = headers; } @Override public InputStream getInputStream() throws IOException { throw new IOException("Only method write(File) is supported."); } public void setContentType(String contentType) { this.contentType = contentType; } @Override public String getContentType() { return contentType; } @Override public String getName() { return localFile.getFileName().toString(); } @Override public boolean isInMemory() { return false; } @Override public byte[] get() { throw new RuntimeException("Only method write(File) is supported."); } @Override public String getString(String encoding) throws UnsupportedEncodingException { throw new RuntimeException("Only method write(File) is supported."); } @Override public String getString() { throw new RuntimeException("Only method write(File) is supported."); } @Override public String getFieldName() { return fieldName; } @Override public void setFieldName(String name) { this.fieldName = name; } @Override public boolean isFormField() { return formField; } @Override public void setFormField(boolean state) { this.formField = state; } @Override public OutputStream getOutputStream() throws IOException { throw new IOException("Only method write(File) is supported."); } } 
0

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like