hi,
in an extension implemented in javascript, i would like to use the
nsIXmlHttpRequest object to query data from an url. based on the
returned content-type, i would like to either parse the returned file
and do some stuff, or save the binary data into a file.
i cannot find a way to save the binary data. the
@mozilla.org/network/file-output-stream;1 component seems not to
implement writeFrom method and i'm running out of ideas.
does anybody have some trick for doing so ?
thanks,
/mig
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFFeBMItMkIv0/ruZgRAtC7AKCijiXKg0w/AYYCHALbhw/gAJt3JACffFVB
bleMcTaaNGl6wW1mBn8LQmI=
=N9Wn
-----END PGP SIGNATURE-----
I found the solution to my problem using binaryinputstream and
binaryoutputstream components.
var channel=request.channel;
var stream=channel.open();
var fos =
Components.classes["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
var length=request.getResponseHeader("content-length");
fos.init(file,0x02 | 0x08 | 0x20, 0644, 0);
var bistream = Components.classes["@mozilla.org/binaryinputstream;1"].
createInstance(Components.interfaces.nsIBinaryInputStream);
bistream.setInputStream(stream);
var bostream = Components.classes["@mozilla.org/binaryoutputstream;1"].
createInstance(Components.interfaces.nsIBinaryOutputStream);
bostream.setOutputStream(fos);
var n=0;
length=parseInt(length);
while(n<length) {
var ba=bistream.readByteArray(bistream.available());
bostream.writeByteArray(ba,ba.length);
n+=ba.length;
}
fos.flush();
fos.close();
stream.close();
Michel Gutierrez wrote:
>
> hi,
>
> in an extension implemented in javascript, i would like to use the
> nsIXmlHttpRequest object to query data from an url. based on the
> returned content-type, i would like to either parse the returned file
> and do some stuff, or save the binary data into a file.
>
> i cannot find a way to save the binary data. the
> @mozilla.org/network/file-output-stream;1 component seems not to
> implement writeFrom method and i'm running out of ideas.
>
> does anybody have some trick for doing so ?
>
> thanks,
> /mig
>
>
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFFeCPmtMkIv0/ruZgRAoCDAKCNCbAA+RSUEIQ6OCGMMO3oGHdDpACeLFut
EZy/9QU/5OqvFNpmfKHS4lI=
=/aSh
-----END PGP SIGNATURE-----
For what it's worth, this does a synchronous load. This is not supported by all
channels, and badly supported by most. Of course you may not care if you make
the UI unusable while your data loads...
-Boris
>> var stream=channel.open();
> For what it's worth, this does a synchronous load. This is not
> supported by all channels, and badly supported by most. Of course you
> may not care if you make the UI unusable while your data loads...
that's right.
i am in asynchronous mode for starting the download, up to getting the
response headers, then i block while downloading the response content. i
don't like this but it is acceptable in my situation as the maximum size
if 20-30KB.
i may try to use channel.asyncOpen with a stream listener later on.
concerning the synchronous load not being always supported, i am only
using http requests through XmlHttpRequest. Is there any chance that
fails in some situations ?
/mig
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFFeEZbtMkIv0/ruZgRAu2vAKCaUp9YX2iDlI7nFzkpDacPX/9FCwCgimBd
mYZwTVwweXSo703zQCo31vI=
=eyKC
-----END PGP SIGNATURE-----
Er.. you mean you call open() on an already-open channel? Or something different?
> concerning the synchronous load not being always supported, i am only
> using http requests through XmlHttpRequest. Is there any chance that
> fails in some situations ?
That probably ought to be OK in new enough Gecko.
-Boris
>> i am in asynchronous mode for starting the download, up to getting the
>> response headers, then i block while downloading the response content.
>
> Er.. you mean you call open() on an already-open channel? Or something
> different?
yes i was calling open from the onload callback of xmlhttprequest.
i throught xmlhttprequest.channel was there for getting the content only
and was not already opened. i get the http headers i need from
xmlhttprequest.getResponseHeader() prior to (re)opening the channel.
i am now trying to get the content in an asynchronous way. this means i
must attach a nsIStreamListener to the channel. you can pass the
listener when you call openAsync, but if the channel is already opened
and openAsync should not be called again, i don't see anything in the
interface to attach the listener afterwards.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFFeFN7tMkIv0/ruZgRAnQeAJ9cK4HvSKfyiU/Xl1XVhLKzGb5QuACgw9Hs
ffWScnJBVw12DmOo1+iLIuY=
=+gbq
-----END PGP SIGNATURE-----
It gets opened when you call send(). If you're in onload, that means that it's
already finished loading and gotten closed. That's the only reason the open()
call is not throwing; I'm not sure whether it should in fact throw (ccing
.network on that).
What the channel _is_ there for is getting the response headers and configuring
the channel _before_ you call send().
So you're basically making two requests to the server, in case you care.
> i am now trying to get the content in an asynchronous way.
The point of XMLHttpRequest is to return the data as 1) a string and 2) a DOM.
Since you want neither one, why are you bothering with XMLHttpRequest? Just
create a channel and use it directly... In your OnStartRequest you can either
set up a pipe, stick the data into it, and then in OnStopRequest parse with a
DOMParser, or set up a stream to write to a file and write.
-Boris
-Boris
> It gets opened when you call send(). If you're in onload, that means
> that it's already finished loading and gotten closed. That's the only
> reason the open() call is not throwing; I'm not sure whether it should
> in fact throw (ccing .network on that).
>
> What the channel _is_ there for is getting the response headers and
> configuring the channel _before_ you call send().
>
> So you're basically making two requests to the server, in case you care.
ok, i catch that. however, given a channel, as i need a nsIInputStream
to access the data, the only way i see to get this stream is through the
open function. i don't see a getInputStream-like method in nsIChannel.
how am i supposed to get this input stream ?
> The point of XMLHttpRequest is to return the data as 1) a string and 2)
> a DOM. Since you want neither one, why are you bothering with
> XMLHttpRequest?
just because i've always done that way and never realize creating the
channel by hand was possible. thanks to you, now i know :)
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFFeGzftMkIv0/ruZgRAnzJAKCFvPc5PEEgfmhN3oDIVl4OBFQ+JgCgjP93
BM/asQdKRm4obIibGH0DuWo=
=PvgG
-----END PGP SIGNATURE-----
asyncOpen, and then the stream is passed to your onDataAvailable whenever data
comes in is what I'd go with.
-Boris
Boris, i followed your advice and implemented the solution using directly a channel in asynchronous mode.
For the records, here is my code in case that can help someone.
Many thanks for your help.
function StreamListener(fileContentType,filePath,callback,callbackArgs) {
this.fileContentType=fileContentType;
this.filePath=filePath;
this.callback=callback;
this.callbackArgs=callbackArgs;
}
StreamListener.prototype={
onStartRequest: function(request,context) {
this.httpChannel=request.QueryInterface(Components.interfaces.nsIHttpChannel);
this.responseStatus=this.httpChannel.responseStatus;
this.responseStatusText=this.httpChannel.responseStatusText;
this.contentType=this.httpChannel.getResponseHeader("content-type");
if(this.contentType==this.fileContentType) {
var file = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(this.filePath);
var fos = Components.classes["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
fos.init(file,0x02 | 0x08 | 0x20, 0644, 0);
this.outputStream = Components.classes["@mozilla.org/binaryoutputstream;1"].
createInstance(Components.interfaces.nsIBinaryOutputStream);
this.outputStream.setOutputStream(fos);
this.type="file";
} else if(this.contentType=="text/xml") {
this.pipe=Components.classes["@mozilla.org/pipe;1"].
createInstance(Components.interfaces.nsIPipe);
this.pipe.init(true,false,1024,10,null);
this.outputStream = Components.classes["@mozilla.org/binaryoutputstream;1"].
createInstance(Components.interfaces.nsIBinaryOutputStream);
this.outputStream.setOutputStream(this.pipe.outputStream);
this.type="xml";
}
},
onDataAvailable: function(request,context,inputStream,offset,count) {
if(this.outputStream!=null) {
var bistream = Components.classes["@mozilla.org/binaryinputstream;1"].
createInstance(Components.interfaces.nsIBinaryInputStream);
bistream.setInputStream(inputStream);
var n=0;
while(n<count) {
var ba=bistream.readByteArray(bistream.available());
this.outputStream.writeByteArray(ba,ba.length);
n+=ba.length;
}
}
},
onStopRequest: function(request,context,nsresult) {
var status=false;
if(this.responseStatus==200) {
if(this.type=="file") {
status=true;
} else if(this.type=="xml") {
var parser=Components.classes["@mozilla.org/xmlextras/domparser;1"].
createInstance(Components.interfaces.nsIDOMParser);
this.xmlDoc=parser.parseFromStream(this.pipe.inputStream,null,this.pipe.inputStream.available(),this.contentType);
}
}
if(this.callback) {
try {
this.callback(status,this.callbackArgs,this);
} catch(e) { dump("Callback exception: "+e+"\n"); }
}
this.outputStream.close();
}
}
var cb=function(status,args,listener) {
if(status) {
// do stuff on success
} else {
// do stuff on failure
}
}
/* url is the address where to get the file or xml document */
/* file is the nsILocalFile where to store the downloaded data */
var ioService = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var channel=ioService.newChannel(url,'',null);
channel.asyncOpen(new StreamListener("application/x-pokereval-refset",file.path,cb,{}),null);
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFFfEpptMkIv0/ruZgRAsQBAJ4gIbhPkwdLEqTDSXlaned9OhUX/QCfbNkj
bOFRR7WwDQzMFXhq3MCjHf8=
=XZRy
-----END PGP SIGNATURE-----
xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", gImageSource, true);
xmlhttp.overrideMimeType('text/plain; charset=x-user-defined');
Results may vary. This was coded up at by Marcus Granado 2006
[http://mgran.blogspot.com]
Then in your onreadystatechange do something like:
var data = xmlhttp.responseText;
var bytes = [];
for (var i=0; i<data.length; i++) {
var c = data.charCodeAt(i);
bytes.push((c > 255) ? c - 63232 : c);
}
At this point, your data will be in the bytes var.
This isn't the right newsgroup, but we should have something like
xmlhttp.rawData or something which contains a byte array of the actual
returned content. I am sure that has been debated in other places.
Doug