javascript - 如何在 firefox 中找到特定的缓存条目并将它们转换为 File 或 Blob 对象?

标签 javascript firefox caching firefox-addon

我有以下场景:

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

我必须解决这个客户端。

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

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

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

最佳答案

Mozilla 版本 2 HTTP 缓存的主要文档位于 here .除了这个页面上的简介之外,我能够理解这个新方案的唯一方法是查看每个对象的实际代码并反向引用几乎所有内容。尽管我无法 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.
}

既然设置了回调对象,我们实际上可以做一些缓存描述符的请求。尝试这样的事情:

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

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

如果您需要更多说明,请随时提出!

关于javascript - 如何在 firefox 中找到特定的缓存条目并将它们转换为 File 或 Blob 对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27842615/

相关文章:

android - 如何解决此问题错误 : Couldn't resolve the package?

javascript - 在 jquery 上禁用缓存

javascript - MaterializeCss - Wave/Ripple 效果手动触发

javascript - 我的 JavaScript 无法正确生成 HTML

javascript - 检测子组件外部点击的简单方法

javascript - 在 HTML <a> 链接中使用 Javascript 变量

javascript - 开发 Firefox 扩展时在页面中执行注入(inject)的 Javascript 代码

css - Windows 上的 Firefox 未加载 @FontFace 资源

c++ - JavaScript 无法访问 XPCOM 对象方法

java - Java中的并发缓存