GeckoFX文件下载

时间:2017-06-02 21:10:33

标签: c# geckofx

我在GeckoFX文件下载方面遇到了一些问题。

我尝试了几个位置描述的LauncherDialog方法,例如How to handle downloading in GeckoFX 29和WebRequest / Webclient方法。

两种方法都有效,但它们对服务器发出两个请求。第一个将触发LauncherDialog.Download事件,LauncherDialog将发出新请求以获取实际文件。

我在客户的自定义Web客户端中使用GeckoFX,在这种特殊情况下,此下载请求需要在服务器端进行几秒钟的处理并修改数据状态。第二个请求被延迟,返回的数据与第一个请求的数据不同。

此外,此特定应用程序不需要任何类型的下载进度窗口。

有没有办法从初始请求中获取数据流?修改GeckoFx-Winforms不是问题。我宁愿避免对GeckoFX-Core进行任何修改,但如果需要,我会这样做。

1 个答案:

答案 0 :(得分:1)

好吧,我重新审视了我对XPCOM编程的错误假设,看了一下Firefox / Gecko源代码中的选定位置,找到了一个解决方案。对于拥有一些XPCOM / XUL编程经验的人来说,这可能是非常明显的,但最初并不适合我。所以我想分享我的解决方案可以帮助一些人。

就我而言,LauncherDialog方法绝对不是可行的方法。

相反,我实施了nsIFactorynsIExternalHelperAppServicensIStreamListener接口。

<强> 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());

就是这样。