在请求某个网址时,我想保存传入的降价信息,将其转换为HTML,然后将其传递以供显示。如果使用观察者,我可以获得频道,并将频道的听众设置为我的特殊"覆盖"通过nsiTraceableChannel监听,然后将数据传递给原始的监听器进行显示,但我很困惑在那一点上做什么。 onDataAvailable方法传递一个nsiInputStream,它无法在javascript代码中读取。虽然我可以将它包装在一个nsiScriptableInputStream中并从中读取,但这似乎会引入很多可能重复多次的简单读取操作。我宁愿用漂亮的封闭二进制代码一次性读取它。
我想要做的是使用NetUtils.asyncCopy将该输入流复制到存储流,并在完成后将存储流的结果转换为传递原始侦听器的内容。但是不会继续使用onDataAvailable调用我的覆盖侦听器吗?文档说onDataAvalilable 必须在返回之前读取inputStream 中的那么多字节,所以我想使用nsiScriptableInputStream是必需的?我是否只是从可编写脚本的输入流中读取,然后忽略并丢弃该异步副本,同时在后台继续执行异步复制? asyncCopy是否用自己的侦听器替换我的覆盖侦听器,这样会好,或者它们是否堆叠,这会不好?
理想情况下,我想要一个接受输出流的东西,并返回一个流侦听器以传递给nsiTraceableChannel.setInputStream,但我找不到那样的东西,甚至是一个实现nsiStreamListener的列表
所以,就像这样:
var {classes: Cc, interfaces: Ci, results: Cr, Constructor: CC, utils: Cu } = Components;
var hack = 3;
/*
0 = use nsiScriptableInputStream
1 = use NetUtil.asyncCopy, and then use nsiScriptableInputStream but ignore
2 = use NetUtil.asyncCopy, and it overrides our own override listener from then on
3 = use NetUtil.asyncCopy, but our own override listener keeps getting onDataAvailable, but we just ignore it
*/
var ScriptableInputStream;
if(hack == 0 || hack == 1) {
ScriptableInputStream = CC("@mozilla.org/scriptableinputstream;1","nsIScriptableInputStream", "init");
}
var StorageStream;
var NetUtil;
if(hack != 0) {
StorageStream = Cc["@mozilla.org/storagestream;1"];
Cu.import("resource://gre/modules/NetUtil.jsm");
}
function HTMLRestyler(tracingChannel) {
this.originalListener = tracingChannel.setNewListener(this);
if(hack == 0) {
this.data = "";
} else {
/* I wonder if creating one of these is as expensive as creating a
nsiScriptableInputStream for every read operation? */
this.storage = StorageStream.createInstance(Ci.nsIStorageStream);
this.storage.init(256,256,null);
this.data = this.storage.getOutputStream(0);
}
}
HTMLRestyler.prototype = {
QueryInterface: function(id)
{
if (id.equals(Components.interfaces.nsIStreamListener) ||
id.equals(Components.interfaces.nsISupportsWeakReference) ||
id.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_NOINTERFACE;
}
onDataAvailable: function(request, context, inputStream, offset, count)
{
if(hack == 0) {
var scriptStream = new ScriptableInputStream(inputStream);
this.data += scriptStream.read(count);
scriptStream.close();
/* the easy way (ow my CPU cycles) */
} else if(hack == 1) {
if(!this.iscopying) {
NetUtils.asyncCopy(inputStream,this.data,this.finished);
this.iscopying = true;
}
/* still have to read the data twice once in asyncCopy, once here
is there any point to doing this? */
var scriptStream = new ScriptableInputStream(inputStream);
var ignored = scriptStream.read(count);
scriptStream.close();
} else if(hack == 2) {
NetUtils.asyncCopy(inputStream,this.data,this.finished);
/* the "best" way
(probably doesn't work)
onDataAvailable and onStopRequest no longer called from here on,
as this listener has been overridden */
} else if(hack == 3) {
if(!this.iscopying) {
NetUtils.asyncCopy(inputStream,this.data,this.finished);
this.iscopying = true;
}
/* but no scriptable input stream needed because it's ok to just ignore
the inputStream here in the override listener and not read data*/
}
}
onStartRequest: function(request, context) {
this.request = request;
this.context = context;
},
onStopRequest: function(request, context, statusCode) {
if(hack != 2) {
this.finished(statusCode);
}
},
finished: function(status) {
this.originalListener.onStartRequest(this.request,this.context);
if(hack != 0) {
var scriptStream = new ScriptableInputStream(this.storage.newInputStream(1));
this.data = scriptStream.read(1000);
this.storage.close();
}
this.originalListener.onDataAvailable(this.transform(this.data));
this.originalListener.onStopRequest(this.request, this.context, status);
},
transform: function(data) {
return "derp "+ data;
}
}
答案 0 :(得分:2)
正如您已经指出的那样,合同onDataAvailable
必须消耗所有数据。因此,异步API是不够的。
这会留下同步API。
nsIStorageStream
或nsIPipe
存储数据直到完成,然后获取js-string。nsIScriptableInputStream
并连接成js-string nsIBinaryInputStream
并连接成一个js-string,八位字节或一个ArrayBuffer。我尝试了很多方法来有效地使用onDataAvailable
中的DownThemAll!来使用数据。在我的用例中,最好在.writeFrom
的输出流端使用nsIPipe
,这不需要先将数据从C ++中提取到JS域。
但是,您的情况可能有所不同:您需要实际修改/转换数据,因此无论如何都需要js-string进行实际转换。将数据存储在某些XPCOM流中,如nsIStorageStream
或nsIPipe
,您仍然可以将整个事物读入js-stream,最后修改它,并将其放回另一个可以传递的流中链接下一个onDataAvailable
听众。这意味着,您有额外的内存开销(存储流和js-string而不仅仅是js-string),而实际上只能节省非常非常少的XPCOM开销。
数组缓冲区也一样。
所以最后,考虑到你的用例,我主张将接收的数据直接连接成一个js-string。但是,你应该自己测量各种选项的时间和记忆,然后决定。
更有可能产生更大的影响,特别是对于内存使用,当然,编写一个有状态的解析器/转换器,不需要先缓存整个响应,而是随时转换。