Отправление файла с помощью XMLHttpRequest
Как отправить файл на сервер с xul-страницы (что-то вроде <input type="file">)? В описаниях указано, что нужно отправлять nsIInputStream, при этом указать соответствующие заголовки и добавить перевод строки. В связи с этим:
- Как добавить что-то к nsIInputStream?
- Как правильно указать параметры при инициализации потока?
void init ( nsIFile file , PRInt32 ioFlags , PRInt32 perm , PRInt32 behaviorFlags ). В описании (http://www.xulplanet.com/refer......ifaces/nsIFileInputStream.html) есть ссылка на какой-то prio.h, но где его взять?
Наконец код, который не работает:
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); var xmlhttpUpload = new XMLHttpRequest(); xmlhttpUpload.open("POST", "/jsps/comp.jsp", false); var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile); file.initWithPath( "C:\\g1.tif" ); xmlhttpUpload.setRequestHeader("Content-type", "image/tif"); xmlhttpUpload.setRequestHeader("Content-length", file.fileSize); var obj_InputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream); obj_InputStream.init(file, 0x01, 00004, null); xmlhttpUpload.send(obj_InputStream);
В последней строке получаем сообщение
[Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsiXMLHttpRequest.send] nsresult: "0x80004005(NS_ERROR_FAILURE)" location: ........." data: no]
/* ... * PRIntn flags * The file status flags. * It is a bitwise OR of the following bit flags (only one of * the first three flags below may be used): * PR_RDONLY Open for reading only. * PR_WRONLY Open for writing only. * PR_RDWR Open for reading and writing. * PR_CREATE_FILE If the file does not exist, the file is created * If the file exists, this flag has no effect. * PR_SYNC If set, each write will wait for both the file data * and file status to be physically updated. * PR_APPEND The file pointer is set to the end of * the file prior to each write. * PR_TRUNCATE If the file exists, its length is truncated to 0. * PR_EXCL With PR_CREATE_FILE, if the file does not exist, * the file is created. If the file already * exists, no action and NULL is returned * * PRIntn mode * The access permission bits of the file mode, if the file is * created when PR_CREATE_FILE is on. ... */ /* Open flags */ #define PR_RDONLY 0x01 #define PR_WRONLY 0x02 #define PR_RDWR 0x04 #define PR_CREATE_FILE 0x08 #define PR_APPEND 0x10 #define PR_TRUNCATE 0x20 #define PR_SYNC 0x40 #define PR_EXCL 0x80 /* ** File modes .... ** ** CAVEAT: 'mode' is currently only applicable on UNIX platforms. ** The 'mode' argument may be ignored by PR_Open on other platforms. ** ** 00400 Read by owner. ** 00200 Write by owner. ** 00100 Execute (search if a directory) by owner. ** 00040 Read by group. ** 00020 Write by group. ** 00010 Execute by group. ** 00004 Read by others. ** 00002 Write by others ** 00001 Execute by others. ** */
Относительно отправки файла. У меня на самом деле не стоит задача полностью воспроизвести <input type="file">, нужно просто отправить файл, необязательно в формате multipart/formdata. И, как я понимаю, проблема не в том, что формат неправильный, а в том, что файл вообще не отправляется. Или я ошибаюсь, и в другом формате отправить нельзя?
Почему не отправляется, если ему дать поток — непонятно, похоже на баг. Если отправлять так, то работает:
... var scriptableStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream); scriptableStream.init(obj_InputStream); var data = ""; while (scriptableStream.available() > 0) data += scriptableStream.read(scriptableStream.available()); scriptableStream.close(); xmlhttpUpload.send(data);
По большому счету, даже если бы получилось, все равно это не решило бы проблемы, т.к. передавать большие файлы по такой схеме нельзя. Впрочем для начала и это бы устроило.
Я порылся в jar-ах в chrome, надеялся там что-нибудь подходящее найти, ведь файлы все-таки как то отправляются, - безуспешно. Вероятно для этого привлекаются средства операционной системы, а не XUL/XPCOM.
Одновременно с этим форумом отправил свой вопрос Нилу Дикину (Neil Deakin) и на форум xulplanet - эффект пока нулевой.
Куда посоветуете обратиться, чтобы был результат? Где-то Вы писали о быстрой реакции Мозиллы на сообщения о багах. Куда надо писать?
Самое главное правило, если вы хотите, чтобы вам помогли: убедитесь, что вы действительно нашли баг. nsIScriptableStream предназначен исключительно для текстовых данных (см. интерфейс — он возвращает char*, то есть ничего удивительного, что он считает знак NUL концом строки). Для чтения двоичных данных используют nsIBinaryStream.
var bufferedStream = Components.classes["@mozilla.org/network/buffered-input-stream;1"] .createInstance(Components.interfaces.nsIBufferedInputStream); bufferedStream.init(obj_InputStream, 16384); xmlhttpUpload.send(bufferedStream);
Все-таки еще не конец (см. п.1 наверху).
Для того, чтобы сервер понял, что ему передается, нужно отделить header запроса от body парой <CR><LF>.
Делается это так:
var multiplexStream = Components.classes["@mozilla.org/io/multiplex-input-stream;1"] .createInstance(Components.interfaces.nsIMultiplexInputStream); var stringStream = Components.classes["@mozilla.org/io/string-input-stream;1"] .createInstance(Components.interfaces.nsIStringInputStream); stringStream.setData("\r", 2); var fileStream = Components.classes["@mozilla.org/network/file-input-stream;1"] .createInstance(Components.interfaces.nsIFileInputStream); fileStream.init(dFile, 0x01, 0444, null); multiplexStream.appendStream(stringStream); multiplexStream.appendStream(fileStream); var bufferedStream = Components.classes["@mozilla.org/network/buffered-input-stream;1"] .createInstance(Components.interfaces.nsIBufferedInputStream); bufferedStream.init(multiplexStream, fileSize); xmlhttpUpload.send(bufferedStream);
Теперь, вроде, все.
Не считая того, что файл должен быть chrome, или подписан, но это отдельный разговор, и, наверное, эта тема уже обсуждалась.
Вы же сами говорили, что собираетесь пересылать большие файлы. Так зачем же вы устанавливаете размер буфера равным размеру файла? В один пакет всё равно больше 1500 байтов не влезет...