Javascript 如何将blob从Chrome扩展传递到Chrome应用程序

Javascript 如何将blob从Chrome扩展传递到Chrome应用程序,javascript,google-chrome,google-chrome-extension,google-chrome-app,Javascript,Google Chrome,Google Chrome Extension,Google Chrome App,一点背景 我已经在一个Chrome扩展上工作了几天,该扩展每天多次拍摄给定网页的屏幕截图。我用它作为向导,一切按预期进行 不过,有一个小的需求扩展无法满足。用户必须有权访问保存图像(屏幕截图)的文件夹,但不能访问。另一方面,Chrome应用程序确实如此。因此,在仔细研究之后,我得出结论,我必须创建一个Chrome扩展和一个Chrome应用程序。其想法是,该扩展将创建一个屏幕截图的blob,然后将该blob发送到应用程序,然后应用程序将其作为图像保存到用户指定的位置。这正是我正在做的——我在扩展端

一点背景

我已经在一个Chrome扩展上工作了几天,该扩展每天多次拍摄给定网页的屏幕截图。我用它作为向导,一切按预期进行

不过,有一个小的需求扩展无法满足。用户必须有权访问保存图像(屏幕截图)的文件夹,但不能访问。另一方面,Chrome应用程序确实如此。因此,在仔细研究之后,我得出结论,我必须创建一个Chrome扩展和一个Chrome应用程序。其想法是,该扩展将创建一个屏幕截图的blob,然后将该blob发送到应用程序,然后应用程序将其作为图像保存到用户指定的位置。这正是我正在做的——我在扩展端创建一个屏幕快照,然后将其发送到应用程序,要求用户选择保存图像的位置

问题

在保存部分,一切都按预期进行。blob在扩展上创建,发送到应用程序,由应用程序接收,询问用户保存位置,并保存图像。。。。这就是事情崩溃的地方生成的图像无法使用。当我试图打开它时,会收到一条消息,上面写着“无法确定类型”。下面是我正在使用的代码:

  • 首先在扩展端,我创建一个blob并将其发送过来,如下所示:

     chrome.runtime.sendMessage(
        APP_ID, /* I got this from the app */
        {myMessage: blob}, /* Blob created previously; it's correct */
        function(response) {
          appendLog("response: "+JSON.stringify(response));
        }
     );
    
    // listen for external messages
    chrome.runtime.onMessageExternal.addListener(
      function(request, sender, sendResponse) {
        if (sender.id in blacklistedIds) {
          sendResponse({"result":"sorry, could not process your message"});
          return;  // don't allow this extension access
        } else if (request.incomingBlob) {
          appendLog("from "+sender.id+": " + request.incomingBlob);
    
          // attempt to save blob to choosen location
          if (_folderEntry == null) {
             // get a directory to save in if not yet chosen
             openDirectory();
          }
          saveBlobToFile(request.incomingBlob, "screenshot.png");
    
          /*
          // inspect object to try to see what's wrong
          var keys = Object.keys(request.incomingBlob);
          var keyString = "";
          for (var key in keys) {
             keyString += " " + key;
          }
          appendLog("Blob object keys:" + keyString);
          */
    
          sendResponse({"result":"Ok, got your message"});
        } else {
          sendResponse({"result":"Ops, I don't understand this message"});
        }
      }
    );
    
  • 然后,在应用程序端,我收到blob并尝试按如下方式保存它:

     chrome.runtime.sendMessage(
        APP_ID, /* I got this from the app */
        {myMessage: blob}, /* Blob created previously; it's correct */
        function(response) {
          appendLog("response: "+JSON.stringify(response));
        }
     );
    
    // listen for external messages
    chrome.runtime.onMessageExternal.addListener(
      function(request, sender, sendResponse) {
        if (sender.id in blacklistedIds) {
          sendResponse({"result":"sorry, could not process your message"});
          return;  // don't allow this extension access
        } else if (request.incomingBlob) {
          appendLog("from "+sender.id+": " + request.incomingBlob);
    
          // attempt to save blob to choosen location
          if (_folderEntry == null) {
             // get a directory to save in if not yet chosen
             openDirectory();
          }
          saveBlobToFile(request.incomingBlob, "screenshot.png");
    
          /*
          // inspect object to try to see what's wrong
          var keys = Object.keys(request.incomingBlob);
          var keyString = "";
          for (var key in keys) {
             keyString += " " + key;
          }
          appendLog("Blob object keys:" + keyString);
          */
    
          sendResponse({"result":"Ok, got your message"});
        } else {
          sendResponse({"result":"Ops, I don't understand this message"});
        }
      }
    );
    
    以下是应用程序上执行实际保存的功能:

    function saveBlobToFile(blob, fileName) {
      appendLog('entering saveBlobToFile function...');
      chrome.fileSystem.getWritableEntry(_folderEntry, function(entry) {         
        entry.getFile(fileName, {create: true}, function(entry) {         
          entry.createWriter(function(writer) {
            //writer.onwrite = function() {
            //   writer.onwrite = null;
            //   writer.truncate(writer.position);
            //};
            appendLog('calling writer.write...');
            writer.write(blob);                       
            // Also tried writer.write(new Blob([blob], {type: 'image/png'}));
          });
        });
      });
    }
    
  • 没有错误。没有打嗝。代码可以工作,但图像没有用处。我到底错过了什么?我哪里做错了?我们是否只能在扩展/应用程序之间传递字符串?这一团在路上被破坏了吗?我的应用程序是否无法访问blob,因为它是在扩展上创建的?谁能帮我解释一下吗

    更新(2014年9月23日) 很抱歉更新太晚,但我被分配到另一个项目,直到2天前才能回到这个项目

    因此,在仔细研究之后,我决定采纳@Danniel Herr的建议,即使用共享工作人员和嵌入在应用程序框架中的页面。其想法是,扩展将向SharedWorker提供blob,SharedWorker将blob转发到扩展中嵌入到应用程序框架中的页面。然后使用parent.postMessage(…)将blob转发到应用程序。这有点麻烦,但似乎这是我唯一的选择

    让我发布一些代码,让它更有意义:

    扩展名:

    var worker = new SharedWorker(chrome.runtime.getURL('shared-worker.js'));
    worker.port.start();
    worker.postMessage('hello from extension'); // Can send blob here too
    worker.port.addEventListener("message", function(event) {
       $('h1Title').innerHTML = event.data;
    });
    
    proxy.js

    var worker = new SharedWorker(chrome.runtime.getURL('shared-worker.js'));
    worker.port.start();
    
    worker.port.addEventListener("message",
       function(event) {      
          parent.postMessage(event.data, 'chrome-extension://[extension id]');
       }
    );
    
    var ports = [];
    var count = 0;
    onconnect = function(event) {
        count++;
        var port = event.ports[0];
        ports.push(port);
        port.start(); 
    
        /* 
        On both the extension and the app, I get count = 1 and ports.length = 1
        I'm running them side by side. This is so maddening!!!
        What am I missing?
        */
        var msg = 'Hi, you are connection #' + count + ". ";
        msg += " There are " + ports.length + " ports open so far."
        port.postMessage(msg);
    
        port.addEventListener("message",       
          function(event) {
            for (var i = 0; i < ports.length; ++i) {
                //if (ports[i] != port) {
                    ports[i].postMessage(event.data);
                //}
            }
        });
    };
    
    proxy.html

    <script src='proxy.js'></script>
    
    这就是执行流程。。。在扩展上,我创建了一个共享worker并向其发送消息。共享工作者应该能够接收blob,但出于测试目的,我只发送一个简单的字符串

    接下来,共享工作者接收消息并将其转发给所有已连接的人。应用程序中框架内的proxy.html/js此时确实已连接,并且应该接收共享工作程序转发的任何内容

    接下来,proxy.js[should]接收来自共享工作者的消息,并使用parent.postMessage(…)将其发送到应用程序。应用程序正在通过一个窗口进行侦听。addEventListener(“消息”,…)

    为了测试这个流程,我首先打开应用程序,然后单击扩展按钮。我在应用程序上没有收到任何消息。我也没有错误

    扩展可以与共享工作进程进行来回通信。该应用程序可以很好地与共享工作者通信。但是,我从扩展->代理->应用发送的消息没有到达应用。我错过了什么

    很抱歉,那些长贴的家伙,但我希望有人能透露一些信息,因为这让我发疯

    谢谢

    我的应用程序是否无法访问blob,因为它是在上创建的 分机?谁能帮我解释一下吗

    没错!您可能希望传递一个
    dataUrl
    ,而不是
    blob
    。下面这样的方法可能会奏效:

    /* Chrome Extension */
    
    var blobToDataURL = function(blob, cb) {
      var reader = new FileReader();
      reader.onload = function() {
        var dataUrl = reader.result;
        var base64 = dataUrl.split(',')[1];
        cb(base64);
      };
      reader.readAsDataURL(blob);
      };
    
    blobToDataUrl(blob, function(dataUrl) {
      chrome.runtime.sendMessage(APP_ID, {databUrl: dataUrl}, function() {});
    });
    
    /* Chrome App */
    
    function dataURLtoBlob(dataURL) {
        var byteString = atob(dataURL.split(',')[1]),
            mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0];
    
        var ab = new ArrayBuffer(byteString.length);
        var ia = new Uint8Array(ab);
        for (var i = 0; i < byteString.length; i++) {
            ia[i] = byteString.charCodeAt(i);
        }
    
        var blob = new Blob([ia], {type: mimeString});
        return blob;
    }
    
    chrome.runtime.onMessageExternal.addListener(
        function(request) {  
      var blob =  dataURLtoBlob(request.dataUrl); 
      saveBlobToFile(blob, "screenshot.png");
    });
    
    /*Chrome扩展*/
    var BLOBTODATURL=函数(blob,cb){
    var reader=new FileReader();
    reader.onload=函数(){
    var dataUrl=reader.result;
    var base64=dataUrl.split(',')[1];
    cb(base64);
    };
    reader.readAsDataURL(blob);
    };
    blobToDataUrl(blob,函数(dataUrl){
    sendMessage(APP_ID,{dataUrl:dataUrl},function(){});
    });
    /*Chrome应用程序*/
    函数dataURLtoBlob(dataURL){
    var byteString=atob(dataURL.split(',')[1]),
    mimeString=dataURL.split(',')[0]。split(':')[1]。split(';')[0];
    var ab=新阵列缓冲区(byteString.length);
    var ia=新的UINT8阵列(ab);
    for(var i=0;i
    这真是个有趣的问题。在我看来,可以使用以下技术来完成:

  • 首先,您应该将blob转换为arraybuffer。这可以通过,并且是异步操作
  • 接下来是一些魔法,目前在稳定的Chrome上可用。因此,将arraybuffer转换为字符串。此操作是同步的
  • 然后,您可以使用ChromeAPI与其他扩展/应用程序进行通信。我正在使用此技术使用另一个著名的传统应用程序来推广我的一个应用程序(新打包应用程序)。由于传统打包应用实际上是扩展,我认为一切都会好起来

  • 我对这个问题非常感兴趣,因为我正试图完成类似的事情

    这些是我发现的问题
    var bytes = new Uint8Array(blobAsBinString.length);
    for (var i=0; i<bytes.length; i++) {
       bytes[i] = blobAsBinString.charCodeAt(i);            
    }             
    blob = new Blob([bytes], {type: mimeString});
    
    function sendBlobToApp() {  
    
      // read the blob in chunks/chunks and send it to the app
      // Note: I crashed the app using 1 KB chunks. 1 MB chunks work just fine. 
      // I decided to use 256 KB as that seems neither too big nor too small
      var CHUNK_SIZE = 256 * 1024;
      var start = 0;
      var stop = CHUNK_SIZE;      
    
      var remainder = blob.size % CHUNK_SIZE;
      var chunks = Math.floor(blob.size / CHUNK_SIZE);      
    
      var chunkIndex = 0;
    
      if (remainder != 0) chunks = chunks + 1;           
    
      var fr = new FileReader();
      fr.onload = function() {
          var message = {
              blobAsText: fr.result,
              mimeString: mimeString,                 
              chunks: chunks 
          };          
          // APP_ID was obtained elsewhere
          chrome.runtime.sendMessage(APP_ID, message, function(result) {
              if (chrome.runtime.lastError) {
                  // Handle error, e.g. app not installed
                  // appendLog is defined elsewhere
                  appendLog("could not send message to app");
              } 
          });
    
          // read the next chunk of bytes
          processChunk();
      };
      fr.onerror = function() { appendLog("An error ocurred while reading file"); };
      processChunk();
    
      function processChunk() {
         chunkIndex++;         
    
         // exit if there are no more chunks
         if (chunkIndex > chunks) {
            return;
         }
    
         if (chunkIndex == chunks && remainder != 0) {
            stop = start + remainder;
         }                           
    
         var blobChunk = blob.slice(start, stop);
    
         // prepare for next chunk
         start = stop;
         stop = stop + CHUNK_SIZE;
    
         // convert chunk as binary string
         fr.readAsBinaryString(blobChunk);
      } 
    }
    
    chrome.runtime.onMessageExternal.addListener(
      function(request, sender, sendResponse) {
        if (sender.id in blacklistedIds) {
          return;  // don't allow this extension access
        } else if (request.blobAsText) {                  
           //new chunk received  
          _chunkIndex++;                   
    
          var bytes = new Uint8Array(request.blobAsText.length);                     
          for (var i=0; i<bytes.length; i++) {
             bytes[i] = request.blobAsText.charCodeAt(i);            
          }         
          // store blob
          _blobs[_chunkIndex-1] = new Blob([bytes], {type: request.mimeString});           
    
          if (_chunkIndex == request.chunks) {                      
             // merge all blob chunks
             for (j=0; j<_blobs.length; j++) {
                var mergedBlob;
                if (j>0) {                  
                   // append blob
                   mergedBlob = new Blob([mergedBlob, _blobs[j]], {type: request.mimeString});
                }
                else {                  
                   mergedBlob = new Blob([_blobs[j]], {type: request.mimeString});
                }
             }                         
    
             saveBlobToFile(mergedBlob, "myImage.png", request.mimeString);
          }
        }
     }
    );