Xpoint
   [напомнить пароль]

Хочется знать, а сколько сейчас байтов передано

Метки: [без меток]
2007-04-17 00:11:55 [обр] dv[досье]
сообщение промодерировано

Доброго всем времени суток.
Думаю что не ошибся с рубрикой.
Я понимаю что браузер в основном создан для закачки файлов и последующего их отображения. Но передо мной встала обратная задача - закачать файл на сервер. Но это еще даже не полбеды. Все условия такие:
 - браузер FF 1.5+
 - Асинхронный XMLHHTPReqest
 - во время закачки (отправки на сервер) получать события о текущем состоянии (onProgress, onStatus)
Вот пожалуй и все. Приведу пример как я пытался это сделать:

Получение потока

function getInputStream(aFile,aBoundary){
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
   var mMultiplexIS = Components.classes["@mozilla.org/io/multiplex-input-stream;1"].createInstance(Components.interfaces.nsIMultiplexInputStream);
   
   // готовлю потоки для последующего заполнения
   var sis1 = Components.classes["@mozilla.org/io/string-input-stream;1"].createInstance(Components.interfaces.nsIStringInputStream);
   var sis2 = Components.classes["@mozilla.org/io/string-input-stream;1"].createInstance(Components.interfaces.nsIStringInputStream);
   var fis = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
   
   // указываю заголовки для части запроса
   var uploadStrStart = '--' + aBoundary + '\r\n';
   uploadStrStart += 'Content-Disposition: form-data; name="test_file_upload"; filename="' + aFile.leafName +'"\r\n';
   uploadStrStart += 'Content-Type: ' + getContentType(aFile) + '\r\n';
   uploadStrStart += '\r\n';
   
   // пишу в строковые потоки подготовленные заголовки
   sis1.setData(uploadStrStart, -1);
   sis2.setData('\r\n', -1);
   // а в файловый подаю объект типа nsILocalFile
   fis.init(aFile, /*MODE_RDONLY*/ 0x01, /*PERMS_FILE*/ 0644, 0);
   
   // 'объединяю' потоки в один
   mMultiplexIS.appendStream(sis1);
   mMultiplexIS.appendStream(fis);
   mMultiplexIS.appendStream(sis2);
   
   // еще парочка потоков...
   var sis = Components.classes["@mozilla.org/io/string-input-stream;1"].createInstance(Components.interfaces.nsIStringInputStream);
   var bis = Components.classes["@mozilla.org/network/buffered-input-stream;1"].createInstance(Components.interfaces.nsIBufferedInputStream);
   
   // для завершения тела запроса
   var uploadStrEnd = '--' + aBoundary + '--';
   sis.setData(uploadStrEnd, -1);
   mMultiplexIS.appendStream(sis);
   
   // кладу все что получилось в буффер
   bis.init(mMultiplexIS,mMultiplexIS.available());
   
   return bis;
}

Определение MIME

function getContentType (aLocalFileInstance){
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
   var mimeService = Components.classes["@mozilla.org/mime;1"].getService(Components.interfaces.nsIMIMEService);
   var contType = "application/octet-stream";
   try {
      contType = mimeService.getTypeFromFile(aLocalFileInstance);
   } catch(ex) {}
   return contType;
}

Собственно сама функция отправки

function sendFile(/*nsILocalFile*/ aFile, /*EventListener*/ aListener, /*EventListener*/ aRSListener, /*nsIInterfaceRequestor*/ aNCBack){
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");

   var mBoundary = "--1234567890--";
   var mXMLHTTPRequest = new XMLHttpRequest();
   
   // открываю запрос (синхронный или нет зависит от переданного параметра) 
   mXMLHTTPRequest.open("POST", "http://localhost/test.php", true);
   
   // запрос асинхронный - устанавливаю слушатели

   aRSListener.mReqInst = mXMLHTTPRequest;
      
   mXMLHTTPRequest.onreadystatechange = aRSListener;
   mXMLHTTPRequest.onprogress = aListener;
   mXMLHTTPRequest.onload = aListener;
   mXMLHTTPRequest.onerror = aListener;
     
         // Попытаюсь спросить у channel текущее состояние
   mXMLHTTPRequest.channel.notificationCallbacks = aNCBack;
   
   
   // устанавливаю заголовок
   mXMLHTTPRequest.setRequestHeader("Content-type", "multipart/form-data; boundary=" + mBoundary);
   
// получаю поток, готовый для отправки
   var bis = getInputStream(aFile,mBoundary);
   
   // ну и собственно отправляю
   mXMLHTTPRequest.send(bis);
}

Теперь о Listener'ах и CallBack'ах

var listener = {
   handleEvent: function(aEvent){
      netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
      try{
         dump(aEvent + "\t" + aEvent.type + "\t" + aEvent.originalTarget + 
            "\n" + aEvent.originalTarget.multipart + "\t" + aEvent.originalTarget.readyState +
            "\n" + aEvent.originalTarget.responseText + "\t" + aEvent.originalTarget.responseXML +
            "\n" + aEvent.originalTarget.status + "\t" + aEvent.originalTarget.statusText +
            "\n" + aEvent.originalTarget.channel.contentLength + "\t" + aEvent.originalTarget.channel.contentCharset);
      } catch(ex){
         dump(ex)
      }
      
   }
}

var RSListener = {
   handleEvent: function(){
      netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
      try{
         dump(this.mReqInst + "\t" + this.mReqInst + 
            "\n" + this.mReqInst.multipart + "\t" + this.mReqInst.readyState +
            "\n" + this.mReqInst.responseText + "\t" + this.mReqInst.responseXML +
            "\n" + this.mReqInst.status + "\t" + this.mReqInst.statusText +
            "\n" + this.mReqInst.channel.contentLength + "\t" + this.mReqInst.channel.contentCharset);
      } catch(ex){
         dump(ex);
      }
   }
}

var notificationCallbacks = {
   // nsIInterfaceRequestor interface
   getInterface: function ( /*nsIIDRef*/ uuid , /*out nsQIResult**/ result ){
      try {
         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
         return this;
      } catch (e) {
         dump("catch:\n" + uuid + "\n" + e);
      }
   },
   onProgress: function(/* nsIRequest*/ request , /*nsISupports*/ context , /*PRUint64*/ progress , /*PRUint64*/ progressMax ){
      netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
      dump("***onProgress\n" + request + "\t" + context + "\n" + progress + "\t" + progressMax);
   },

   onStatus: function ( /*nsIRequest*/ request , /*nsISupports*/ context , /*nsresult*/ status , /*PRUnichar**/ statusArg ){
      netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
      dump("***onStatus\n" + request + "\t" + context + "\n" + status + "\t" + statusArg);
   }
}

И напоследок как я все это вызываю

function sendASync(){
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
   var obj = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
   obj.initWithPath("c:\\test.txt");
   
   sendFile(obj,listener,RSListener,notificationCallbacks);
}

Для проверки сделать что-нибудь вроде

<input type="button" value="send" onclick="sendASync()"/>
<button label="send" oncommand="sendASync()"/>

Понимаю, что написал много. Но старался, чтоб легко читалось, и так, как есть у меня.

Что в результате у меня получается. Отправка файла идет, индикатор загруженности сети показывает 95%.
Выскакивает ошибка

[Exception... "Component returned failure code: 0x80040111 (NS_ERROR_NOT_AVAILABLE) [nsIXMLHttpRequest.status]"
nsresult: "0x80040111 (NS_ERROR_NOT_AVAILABLE)"
location: "JS frame :: file:///C:/test.js :: anonymous :: line 107" data: no]

И все. И FF умолкает, будь он не ладен. А загрузка-то продолжается.
Как только файл отправился, сразу листенеры начинают мне кричать: все OK, status 200.
Но мне-то нужен не ответ сервера, а сам процесс загрузки.

Думаю что дело в неправильной установке NotificationCallBack. Но если не так, то как же тогда?
Ломаю голову второй день.

Если кто сможет разобраться в чем же дело, а может и вообще подскажет другой, может быть более разумный, путь решения, буду премного благодарен.

С уважением, DV.

спустя 3 часа 49 минут [обр] Владимир Палант(434/4445)[досье]
Да уж, тяжелый случай... А как насчет того, чтобы отслеживать чтение из потока, который получает XMLHttpRequest? Идеально было бы использовать nsIStreamListenerTee, но похоже, что его нельзя создать из JavaScript. Поэтому вижу только вариант создать nsIPipe — в XMLHttpRequest отдать один конец, а в другой писать данные из nsIMultiplexInputStream. Извращение получается, конечно, но не кривее альтернатив.
спустя 1 день 16 часов [обр] dv[досье]

Владимир Палант[досье]
Тяжелый - не тяжелый, но реализуемый. Сегодня наконец вышло. Правда без XMLHttpRequest. Попробовал сделать как вы предложили, ... действительно "Извращение получается". Все оказалось очень просто. Создал nsIChannel, установил у него notificationCallbacks. И все, вуаля. Осталось только asyncOpen() сказать. onProgress'ы и onStatus'ы сыпятся как из рога изобилия.
Если кому интересно могу привести рабочий код.

PS. Странно только, что похожая ситуация с XMLHHTPRequest не прокатывает. По логике вещей делается тоже самое.

спустя 22 часа [обр] sndralex(2/2)[досье]
Рабочий код интересует. Приведите пожалуйста
спустя 2 года 9 месяцев [обр] VaDima(0/1)[досье]
А как бы все-таки ознакомиться с вашим решением?
Спасибо
Powered by POEM™ Engine Copyright © 2002-2005