У меня была точно такая же проблема. Проблема заключается в том, как играть! обрабатывает многочастные загрузки. Обычно вы можете добавить FileUpload в ваш метод загрузки и получить ваши файлы там. Это очень помогает, так как вы можете получить имена файлов, размеры и все эти вещи прямо из Play:
public static void uploadFile(File fileUpload) {
String name = fileUpload.getName() // etc.
}
Однако использование этой логики не позволяет использовать HTTPRequest. Поэтому, если вы используете способ загрузки файлов без использования Play (например, с помощью XMLHTTPRequest), когда автоматическое сопоставление с fileUpload не будет работать, происходит следующее:
- Play пытается связать запрос с вашими аргументами
- Play встречает ваш аргумент File и анализирует запрос.
- Play не находит ничего полезного (так как он не понимает XMLHttpRequest) и отображает ваш аргумент File в null.
Теперь поток ввода запроса уже используется Play, и вы получаете сообщение "Bad File Descriptor".
Решение этой проблемы - не использовать Play! Волшебство, если вы хотите использовать тот же самый метод для загрузки через форму и XMLHttpRequest (XHR). Я хотел использовать скрипт загрузки файлов Valum (http://github.com/valums/file-uploader) в дополнение к моему собственному методу загрузки на основе форм. Один использует XHR, другой использует простые многочастные загрузки форм. Я создал следующий метод в моем контроллере, который берет загруженный файл из Параметр "qqfile" и работает с формой и XHR-загрузками:
@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;
}
}
Надеюсь, это поможет, мне понадобилось около дня, чтобы сделать эту работу с моей стороны.