Upload a File in a Google Chrome Extension
Asked Answered
I

3

30

I'm writing an extension for Chrome, and I need to upload a file from the page the user is currently on to my server to be processed, I cannot figure out how to upload the file though. I considered just passing the link to the server and having the server download the file, however if the site requires authentication this will not work. Is it possible to upload a file via a Chrome extension to my server?

Illogic answered 4/11, 2010 at 3:40 Comment(0)
P
43

I've recently developed a Chrome extension which retrieves content from a page, and sends it to the server.

The following approach was used:

  1. File downloads: Get the src property of an <img> element, for example.
  2. Fetch the file from the Cache - use XMLHttpRequest from the background page.
  3. Use a Web Worker in the background page to handle the upload.

Side note, to take the checksum of the image, Crypto-JS: MD5 can be used. Example (where xhr is the XMLHttpRequest object with responseType set to arraybuffer, see Worker demo):

var md5sum = Crypto.MD5( new Uint8Array(xhr.response) );

Full example

Content script

// Example: Grab the first <img> from the document if it exists.
var img = document.images[0];
if (img) {
    // Send the target of the image:
    chrome.runtime.sendMessage({method: 'postUrl', url: img.src});
}

Background script (with Worker)

chrome.runtime.onMessage.addListener(function(request) {
    if (request.method == 'postUrl') {
        var worker = new Worker('worker.js');
        worker.postMessage(request.url);
    }
});

Web Worker

// Define the FormData object for the Web worker:
importScripts('xhr2-FormData.js')

// Note: In a Web worker, the global object is called "self" instead of "window"
self.onmessage = function(event) {
    var resourceUrl = event.data;   // From the background page
    var xhr = new XMLHttpRequest();
    xhr.open('GET', resourceUrl, true);

    // Response type arraybuffer - XMLHttpRequest 2
    xhr.responseType = 'arraybuffer';
    xhr.onload = function(e) {
        if (xhr.status == 200) {
            nextStep(xhr.response);
        }
    };
    xhr.send();
};
function nextStep(arrayBuffer) {
    var xhr = new XMLHttpRequest();
    // Using FormData polyfill for Web workers!
    var fd = new FormData();
    fd.append('server-method', 'upload');

    // The native FormData.append method ONLY takes Blobs, Files or strings
    // The FormData for Web workers polyfill can also deal with array buffers
    fd.append('file', arrayBuffer);

    xhr.open('POST', 'http://YOUR.DOMAIN.HERE/posturl.php', true);

    // Transmit the form to the server
    xhr.send(fd);
};

FormData for Web workers POLYFILL

Web workers do not natively support the FormData object, used to transmit multipart/form-data forms. That's why I've written a polyfill for it. This code has to be included in the Web worker, using importScripts('xhr2-FormData.js').

The source code of the polyfill is available at https://gist.github.com/Rob--W/8b5adedd84c0d36aba64

Manifest file:

{
  "name": "Rob W - Demo: Scraping images and posting data",
  "version": "1.0",
  "manifest_version": 2,
  "content_scripts": [
    {
      "matches": ["http://*/*", "https://*/*"],
      "js": ["contentscript.js"]
    }
   ],
   "background": {
       "scripts": ["background.js"]
   },
   "permissions": ["http://*/*", "https://*/*"]
}

Relevant documentation

Polo answered 3/4, 2012 at 22:40 Comment(11)
Rob this is great stuff, thanks for posting such a detailed response. I notice that in this other thread you recommend not using a Web Worker if you're sending larger files. Would you still recommending keeping the XMLHttpRequest in the background process (not using a web worker) for a chrome extension if the files in question are going to be 500+kb?Passementerie
Also, how do you make sure the arraybuffer response is coming from the cache and not making a new request?Passementerie
@Passementerie I suggest to not use a Web Worker, unless you're going to do some expensive blocking IO, or doing some CPU-intensive processing. XMLHttpRequest offers little control over caching behavior, but the fetch API provides some means to control this.Polo
I feel like you are hand waving the most important part - getting the content (e.g. image) from the page. You say "Fetch the file from the Cache - use XMLHttpRequest from the background page." but then call this into question when you add "XMLHttpRequest offers little control over caching behavior".Prosser
and to add, for my use case this is very important because the content on the page may not be possible to be fetched (again) using the URL in the page (either due to bandwidth limitations or expiration of the original)Prosser
@Prosser You can't retrieve something that does not exist. If the file is not cached, then you cannot use the network to fetch it, hence my answer assumes that the file is cached, or at the very least retrievable from the network. If it is very important to get the file data, then you have to intercept the response in advance (there is no standard API for this), or (if the exact byte representation is not important) take a screenshot with the chrome.tabs.captureVisibleTab API and reconstruct the image.Polo
I guess my thinking was, suppose you have an <IMG> in the browser currently being displayed. If it's displayed it must be cached somewhere, right? So are you saying in this case I can expect that XMLHttpRequest won't hit the network to try to reload it?Prosser
@Prosser You cannot expect the file to be cached. If the server explicitly requests no-cache behavior, then the browser may discard the response as soon as it has received used it (e.g. to paint on the screen).Polo
Ah I see. So in that case, it's going to make the request, but presumably under the same conditions as the original request? (e.g. same credentials, referrer, etc.?)Prosser
Or I guess to put it another way, there isn't any other way for a Chrome extension to get the raw contents of an object such as <IMG> on the page?Prosser
@Prosser The request is new and not guaranteed to be associated with the previous request. There is no guaranteed way to get the "raw contents of an object such as <IMG> on the page". The only way to achieve your desired goal is to intercept the request in advance. The way to do that is outside the scope of this answer / comment chain, since a general way is very complex and/or expensive.Polo
H
1

The simplest solutions seems to be for your extension to send the file's URI to your server, and then your server-side code will download it from the page into the server and process it.

Create a server-side script like http://mysite.com/process.php?uri=[file's URI goes here] that will process the given file. Use AJAX to call this URL (more info at http://code.google.com/chrome/extensions/xhr.html ). The script will return the processed file, which you could then use in your extension.

Hermaphroditism answered 16/11, 2010 at 1:15 Comment(2)
Like I said in my question that only works for files that are not behind logins/paywalls.Illogic
Link is not openingPhil
M
0

You should checkout the following:

chrome.extension.sendRequest() and chrome.extension.onRequest()

You can read more about them here: http://code.google.com/chrome/extensions/messaging.html

Basically you will setup the page on the server to watch for the Chrome extension, and once they connect you will need to have a javascript that will do the upload task for you.

I haven't tested this out, but it may get you where you need to be. Also you may want to read the Long-lived connections section.

Goodluck

Melitamelitopol answered 13/11, 2010 at 7:25 Comment(2)
I think you've misunderstood the task I'm trying to accomplish. The plugin will allow a user to upload a file (say a PDF) from any website to mine to be reprocessed. I'd like this to happen without the user having to first download the file, then upload it to me. Currently the plugin finds all of the documents (of the correct type) and lists them in the plugin's popup. I want the user to simply be able to choose one from the popup and have it upload automatically. I may not control the page/server where the user is currently navigated to.Illogic
Ok, that makes sense, and you are right I misunderstood your intentions. There is a plugin I used with a similar feature, it would list all Flash videos on the site you are looking at, and would allow you to upload them to another site. It was my old laptop, and I dont have it anymore, will look for it to see if there is anything there that might help.Melitamelitopol

© 2022 - 2024 — McMap. All rights reserved.