如何在 Firefox 中查找特定的缓存条目并将其转换为 File 或 Blob 对象?

5 javascript firefox caching firefox-addon

我有以下场景:

用户可以将 html 内容粘贴到所见即所得编辑器中。当粘贴的内容包含托管在其他域上的图像时,我希望将它们上传到我的服务器。目前唯一的方法是通过“将图像另存为...”上下文菜单手动下载,然后通过表单将图像上传到服务器并在编辑器中更新图像。

我必须解决这个客户端问题。

我正在开发一个可以自动化该过程的 Firefox 插件。当然,我可以下载这些图像,将它们存储在硬盘上,然后使用 FormData 或更好的 pupload 上传它们,但这似乎很笨拙,因为内容显示在浏览器中,它必须已经下载并驻留在内存中的某个位置。我想从内存中抓取图像文件并告诉 Firefox 上传它们(能够制作它们的 Blob 似乎就足够了)。

然而,我在 MDN 上几个不同缓存系统的 API 文档中迷失了方向,无法找到任何如何使用它们的示例代码。我检查了访问缓存的其他插件的代码,但大多数都没有注释并且仍然相当神秘。

您能否向我指出一些示例代码,说明实现此目标的推荐方法是什么?最好的解决方案是,如果我可以从 firefox 请求特定的 url,这样我就可以在 FormData 中使用它,如果它不在缓存中,firefox 就会下载到内存中,但如果它已经存在,我就直接获取它。

Chu*_*k R 5

Mozilla 版本 2 HTTP 缓存的主文档位于此处。除了本页上的简介之外,我能够理解这个新方案的唯一方法是查看每个对象的实际代码并反向引用几乎所有内容。尽管我无法 100% 清楚地了解到底发生了什么,但我已经弄清楚了足够的信息来让它发挥作用。在我看来,Mozilla 在推出新 API 之前应该花时间创建一些简单术语的文档。但是,我想我们得到了他们给我们的东西。

解决你的问题。我们假设想要上传图像的用户已经将该图像保存在其缓存中的某个位置。为了能够将其从用户的缓存中拉出进行上传,您必须首先能够确定图像的 URI,然后才能从缓存中显式拉出图像。为了简洁起见,我假设您已经弄清楚了这一部分。

关于新的 HTTP 缓存,需要注意的一个重要事项是,尽管它全部基于回调,但仍然只能有一个写入过程。虽然在您的示例中可能不需要写入描述符,但您仍然应该请求写入访问权限,因为这将阻止任何其他进程(即浏览器)更改/删除数据,直到您完成为止。另一个旁注和对我来说很多痛苦的根源是,从内存缓存请求缓存条目将始终创建一个新条目,覆盖任何预先存在的条目。你不应该需要这个,但如果有必要,你可以从磁盘访问内存缓存(磁盘缓存是物理磁盘+内存缓存——Mozilla 逻辑)缓存,而不会产生副作用。

一旦获得 URI,您就可以发出请求将其从缓存中拉出。新的缓存系统完全基于回调。为了能够获取缓存条目的数据,我们需要一个关键对象—— nsICacheEntryOpenCallback。这是一个用户定义的对象,用于在请求缓存条目后处理响应。它必须有两个成员函数:onCacheEntryCheck(entry, appcache) 和 onCacheEntryAvilable(descriptor, isnew, appcache, status)。

这是我的此类对象代码的简化示例:

var cacheWaiter = {
  //This function essentially tells the cache service whether or not we want
  //this cache descriptor. If ENTRY_WANTED is returned, the cache descriptor is
  //passed to onCacheEntryAvailable()
  onCacheEntryCheck: function( descriptor, appcache )
  {
    //First, we want to be sure the cache entry is not currently being written
    //so that we can be sure that the file is complete when we go to open it.
    //If predictedDataSize > dataSize, chances are it's still in the process of
    //being cached and we won't be able to get an exclusive lock on it and it
    //will be incomplete, so we don't want it right now.
    try{
      if( descriptor.dataSize < descriptor.predictedDataSize )
        //This tells the nsICacheService to call this function again once the
        //currently writing process is done writing the cache entry.
        return Components.interfaces.nsICacheEntryOpenCallback.RECHECK_AFTER_WRITE_FINISHED;
    }
    catch(e){
      //Also return the same value for any other error
      return Components.interfaces.nsICacheEntryOpenCallback.RECHECK_AFTER_WRITE_FINISHED;
    }
    //If no exceptions occurred and predictedDataSize == dataSize, tell the
    //nsICacheService to pass the descriptor to this.onCacheEntryAvailable()
    return Components.interfaces.nsICacheEntryOpenCallback.ENTRY_WANTED;
  }

  //Once we are certain we want to use this descriptor (i.e. it is done
  //downloading and we want to read it), it gets passed to this function
  //where we can do what we wish with it.
  //At this point we will have full control of the descriptor until this
  //function exits (or, I believe that's how it works)
  onCacheEntryAvailable: function( descriptor, isnew, appcache, status )
  {
    //In this function, you can do your cache descriptor reads and store
    //it in a Blob() for upload. I haven't actually tested the code I put
    //here, modifications may be needed.
    var cacheentryinputstream = descriptor.openInputStream(0);
    var blobarray = new Array(0);
    var buffer = new Array(1024);      

    for( var i = descriptor.dataSize; i == 0; i -= 1024)
    {
      var chunksize = 1024;
      if( i < 0 )
        chunksize = 1024 + i;
      try{
        cacheentryinputstream.read( buffer, chunksize );
      }
      catch(e){
        //Nasty NS_ERROR_WOULD_BLOCK exceptions seem to happen to me
        //frequently. The Mozilla guys don't provide a way around this,
        //since they want a responsive UI at all costs. So, just keep
        //trying until it succeeds.
        i += 1024;
        continue;
      }
      for( var j = 0; j < chunksize; j++ )
      {
        blobarray.push(buffer.charAt(j));
      }
      if( i < 0 )
        i = 0 //Set i == 0 to signal loop break
    }
  }
  var theblob = new Blob(blobarray);
  //Do an AJAX POST request here.
}
Run Code Online (Sandbox Code Playgroud)

现在回调对象已经设置完毕,我们实际上可以对缓存描述符进行一些请求。尝试这样的事情:

var theuri = "http://www.example.com/image.jpg";

//Load the cache service
var cacheservice = Components.classes["@mozilla.org/netwerk/cache-storage-service;1"].getService(Components.interfaces.nsICacheStorageService)

//Select the default disk cache.
var hdcache = cacheservice.diskCacheStorage(Services.loadContextInfo.default, true);

//Request a cache entry for the URI. OPEN_NORMALLY requests write access.
hdcache.asyncOpenURI(ioservice.newURI(theuri, null, null), "", hdcache.OPEN_NORMALLY, cacheWaiter);
Run Code Online (Sandbox Code Playgroud)

至于实际获取 URI,您可以提供一个窗口,供用户将图像拖放到其中,或者可能只是将图像的 URL 粘贴到其中。然后,您可以执行 AJAX 请求来获取图像(如果用户由于某种原因实际上没有访问该图像,则会将其缓存)。然后,您可以使用该 URL 获取缓存条目以进行上传。作为一种审美接触,您甚至可以显示图像的预览,但这有点超出了问题的范围。

如果您需要更多说明,请随时询问!