Java : InputStream 到 Multi-part 文件转换,结果文件为空
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33625906/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java : InputStream to Multi-part file conversion, result file is empty
提问by We are Borg
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.
我正在开发一个 Java 应用程序,我试图在其中从下载的 InputStream 创建一个 Multipart 文件。不幸的是,它不起作用并且 Multipart 文件是空的。在将其复制到 Multipart 之前,我检查了磁盘上 savedFile 的大小,它具有正确的大小、属性和内容。
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.
我在转换中做错了什么?你能帮忙的话,我会很高兴。非常感谢。
采纳答案by vanOekel
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
的DiskFileItem
使用DeferredFileOutputStream
,它使用一个内存中的字节数组当字节被实际传送,其仅填充。由于直接使用文件并且实际上没有复制字节,因此永远不会填充字节数组。在源代码中自己查看:
源代码CommonsMultipartFile
源代码DiskFileItem
源代码DeferredFileOutputStream
So, instead of just calling fileItem.getOutputStream();
, transfer the bytes to fill the in-memory byte-array:
因此,不要只调用 ,而是fileItem.getOutputStream();
传输字节以填充内存中的字节数组:
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
:
然后tranferTo
呼叫将起作用。
这似乎是有点麻烦的只是移动文件:CommonsMultipartFile
只要求fileItem.write((File)dest)
在transferTo
方法。下面是两个测试用例,一个使用DiskFileItem
,一个使用LocalFileItem
. 代码LocalFileItem
如下所示。
我使用的依赖org.springframework:spring-web:4.2.2.RELEASE
,commons-fileupload:commons-fileupload:1.3.1
以及junit:junit:4.12
测试类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!
自定义LocalFileItem
类,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.");
}
}