我遇到了完全相同的问题。问题在于Play的方式!处理分段上传。通常您可以将 FileUpload 添加到您的上传方法并在那里获取您的文件。这很有帮助,因为您可以直接从 Play 获取文件名和大小以及所有这些内容:
public static void uploadFile(File fileUpload) {
String name = fileUpload.getName() // etc.
}
但是,使用此逻辑会阻止您使用 HTTPRequest。因此,如果您使用非播放方式上传文件(例如使用 XMLHTTPRequest),而自动映射到 fileUpload 将不起作用,则会发生以下情况:
- Play 尝试将请求绑定到您的参数
- Play 遇到您的 File 参数并解析请求。
- Play 找不到任何用处(因为它不理解 XMLHttpRequest)并将您的 File 参数映射为 null。
现在请求输入流已经被 Play 消费了,并且您收到了“错误的文件描述符”消息。
解决方案是不使用任何 Play!魔术,如果您想使用 same 方法通过 Form 和 XMLHttpRequest (XHR) 上传。除了我自己的基于表单的上传方法之外,我还想使用 Valum 的文件上传脚本 (http://github.com/valums/file-uploader)。一种使用 XHR,另一种使用普通的多部分表单上传。我在控制器中创建了以下方法,该方法从“qqfile”参数中获取上传的文件,并与基于表单和 XHR-Uploads 一起使用:
@SuppressWarnings({"UnusedDeclaration"})
public static void uploadFile() {
FileUpload qqfile = null;
DataParser parser = DataParser.parsers.get(request.contentType);
if (parser != null) {
// normal upload. I have to manually parse this because
// play kills the body input stream for XHR-requests when I put the file upload as a method
// argument to {@link #uploadFile)
parser.parse(request.body);
@SuppressWarnings({"unchecked"})
ArrayList<FileUpload> uploads = (ArrayList<FileUpload>) request.args.get("__UPLOADS");
for (FileUpload upload : uploads) {
if ("qqfile".equals(upload.getFieldName())) {
qqfile = upload;
break;
}
}
} else {
// XHR upload
qqfile = new FileUpload(new XHRFileItem("qqfile"));
}
if (qqfile == null) {
badRequest();
return;
}
// and now do something with your Fileupload object here (e.g. write it to db or something else)
}
如果将此方法一分为二,您可能可以跳过 if 的 IF 部分,这样您就可以使用正常的 Play!默认上传的魔法,并为您的 XHR 上传使用单独的方法。
我还必须创建 XHRFileItem 类,该类只包含通过 XMLHttpRequest 发布的文件项。您可能需要对其进行一些修改以处理多个文件和您的特定文件上传器,但它仍然是:
package application.util;
import org.apache.commons.fileupload.FileItem;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import static play.mvc.Http.Request.current;
/**
* An implementation of FileItem to deal with XmlHttpRequest file uploads.
*/
public class XHRFileItem implements FileItem {
private String fieldName;
public XHRFileItem(String fieldName) {
this.fieldName = fieldName;
}
public InputStream getInputStream() throws IOException {
return current().body;
}
public String getContentType() {
return current().contentType;
}
public String getName() {
String fileName = current().params.get(fieldName);
if (fileName == null) {
fileName = current().headers.get("x-file-name").value();
}
return fileName;
}
public boolean isInMemory() {
return false;
}
public long getSize() {
return 0;
}
public byte[] get() {
return new byte[0];
}
public String getString(String s) throws UnsupportedEncodingException {
return s;
}
public String getString() {
return "";
}
public void write(File file) throws Exception {
FileOutputStream fos = new FileOutputStream(file);
InputStream is = getInputStream();
byte[] buf = new byte[64000];
int read;
while ((read = is.read(buf)) != -1) {
fos.write(buf, 0, read);
}
fos.close();
}
public void delete() {
}
public String getFieldName() {
return fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
public boolean isFormField() {
return false;
}
public void setFormField(boolean b) {
}
@Nullable
public OutputStream getOutputStream() throws IOException {
return null;
}
}
希望这会有所帮助,我花了大约一天的时间才完成这项工作。