我在GeckoFX文件下载方面遇到了一些问题。
我尝试了几个位置描述的LauncherDialog方法,例如How to handle downloading in GeckoFX 29和WebRequest / Webclient方法。
两种方法都有效,但它们对服务器发出两个请求。第一个将触发LauncherDialog.Download事件,LauncherDialog将发出新请求以获取实际文件。
我在客户的自定义Web客户端中使用GeckoFX,在这种特殊情况下,此下载请求需要在服务器端进行几秒钟的处理并修改数据状态。第二个请求被延迟,返回的数据与第一个请求的数据不同。
此外,此特定应用程序不需要任何类型的下载进度窗口。
有没有办法从初始请求中获取数据流?修改GeckoFx-Winforms不是问题。我宁愿避免对GeckoFX-Core进行任何修改,但如果需要,我会这样做。
答案 0 :(得分:1)
好吧,我重新审视了我对XPCOM编程的错误假设,看了一下Firefox / Gecko源代码中的选定位置,找到了一个解决方案。对于拥有一些XPCOM / XUL编程经验的人来说,这可能是非常明显的,但最初并不适合我。所以我想分享我的解决方案可以帮助一些人。
就我而言,LauncherDialog
方法绝对不是可行的方法。
相反,我实施了nsIFactory
,nsIExternalHelperAppService
和nsIStreamListener
接口。
<强> nsiStreamListener 强>
internal class MyStreamListener : nsIStreamListener
{
public MyStreamListener(/*...*/) { }
public void OnStartRequest(nsIRequest aRequest, nsISupports aContext)
{
// This will get called once, when the download "begins".
// You can initialize your things here.
}
public void OnStopRequest(nsIRequest aRequest, nsISupports aContext, int aStatusCode)
{
// This will also get called once, when the download is
// complete or interrupted. You can perform the post-download
// actions here.
if (aStatusCode != GeckoError.NS_OK) {
// download interrupted
}
else {
// download completed
}
}
public void OnDataAvailable(nsIRequest aRequest, nsISupports aContext, nsIInputStream aInputStream, ulong aOffset, uint aCount)
{
// This gets called several times with small chunks of data.
// Do what you need with the stream. In my case, I read it
// in a small buffer, which then gets written to an output
// filestream (not shown).
// The aOffset parameter is the sum of all previously received data.
var lInput = InputStream.Create(aInputStream);
byte[] lBuffer = new byte[aCount];
lInput.Read(lBuffer, 0, (int)aCount);
}
}
<强> nsIExternalHelperAppService 强>
public class MyExternalHelperAppService : nsIExternalHelperAppService
{
public MyExternalHelperAppService(/* ... */)
{
/* ... */
}
public nsIStreamListener DoContent(nsACStringBase aMimeContentType, nsIRequest aRequest, nsIInterfaceRequestor aWindowContext, bool aForceSave)
{
var request = Request.CreateRequest(aRequest);
var lChannel = request as HttpChannel;
try {
if (lChannel != null) {
var uri = lChannel.OriginalUri;
var contentType = lChannel.ContentType;
var contentLength = lChannel.ContentLength;
var dispositionFilename = lChannel.ContentDispositionFilename;
// Do your contenttype validation, keeping only what you need.
// Make sure you clean dispositionFilename before using it.
// If you don't want to do anything with that file, you can return null;
return new MyStreamListener(/* ... */);
}
}
catch (COMException) {
/* ... */
}
return null;
}
}
nsIFactory (您还可以重载GenericOneClassNsFactory&lt; TFactory,TType&gt;):
public IntPtr CreateInstance(nsISupports aOuter, ref Guid iid)
{
// This is called when the content dispatcher gets a DISPOSITION_ATTACHMENT
// on the channel, or when it doesn't have any builtin handler
// for the content type. It needs an external helper to handle
// the content, so it creates one and calls DoContent on it.
MyExternalHelperAppService _myExternalHelperAppService = new MyExternalHelperAppService(...);
IntPtr result;
IntPtr iUnknownForObject = Marshal.GetIUnknownForObject(_myExternalHelperAppService);
Marshal.QueryInterface(iUnknownForObject, ref iid, out result);
Marshal.Release(iUnknownForObject);
return result;
}
public void LockFactory(bool @lock) {
// do nothing here, it's not used, only kept for backwards compatibility.
}
然后,在我的初始化代码中的某个地方,我使用正确的合同注册了我的nsIFactory
:
Xpcom.RegisterFactory(typeof(MyExternalHelperAppService).GUID,
"MyExternalHelperAppService",
"@mozilla.org/uriloader/external-helper-app-service;1",
new MyNsFactory());
就是这样。