• Skip to main content
  • Select language
  • Skip to search
MDN Web Docs
  • Technologies
    • HTML
    • CSS
    • JavaScript
    • Graphics
    • HTTP
    • APIs / DOM
    • WebExtensions
    • MathML
  • References & Guides
    • Learn web development
    • Tutorials
    • References
    • Developer Guides
    • Accessibility
    • Game development
    • ...more docs
Add-ons
  1. MDN
  2. Mozilla
  3. Add-ons
  4. Browser extensions
  5. Working with files

Working with files

In This Article
  1. Download files using the downloads API
  2. Open files in an extension using a file picker
  3. Open files in an extension using drag and drop
  4. Store files data locally using the IndexedDB file storage library
    1. Creating the store and saving the images
    2. Retrieving stored images for display
    3. Delete collected images
  5. Process files in a local app

Your browser extension may need to work with files to deliver its full functionality. This article looks at the five mechanisms you have for handling files:

  • Downloading files to the user’s selected download folder.
  • Opening files using a file picker on a web page.
  • Opening files using drag and drop onto a web page.
  • Storing files or blobs locally with IndexedDB using the idb-file-storage library.
  • Passing files to a native application on the user’s computer.

For each of these mechanisms, we introduce their use with references to the relevant API documentation, guides, and any examples that show how to use the API.

Download files using the downloads API

This mechanism enables you to get a file from your website (or any location you can define as a URL) to the user’s computer. The key method is downloads.download(), which in its simplest form accepts a URL and downloads the file from that URL to the user’s default downloads folder:

browser.downloads.download({ url : ‘https://example.org/image.png’ })

You can let the user download to a location of their choice by specifying the saveAs parameter.

Using URL.createObjectURL() you can also download files and blobs defined in your JavaScript, which can include local content retrieved from IndexedDB.

The downloads API also provides features to cancel, pause, resume, erase, and remove downloads; search for downloaded files in the download manager; show downloaded files in the computer’s file manager; and open a file in an associated application.

To use this API you need to have the "downloads" API permission specified in your manifest.json file.

Example: Latest download
API reference: downloads API

Open files in an extension using a file picker

If you want to work with a file from the user’s computer one option is to let the user select a file using the computer’s file browser. Either create a new page or inject code into an existing page to use the file type of the HTML input element to offer the user a file picker. Once the user has picked a file or files the script associated with the page can access the content of the file using the DOM File API, in the same way a web application does.

Example: Imagify
Guide: Using files from web applications
API references: HTML input element | DOM File API

If you want to access or process all the files in a selected folder, you can do so using <input type="file" webkitdirectory="true"/> to select the folder and return all the files it contains.

Open files in an extension using drag and drop

The Web Drag and Drop API offers an alternative to using a file picker. To use this method, establish a ‘drop zone’ that fits with your UI, then add listeners for the dragenter, dragover, and drop events to the element. In the handler for the drop event, your code can access any file dropped by the user from the object offered by the dataTransfer property using DataTransfer.files. Your code can then access and manipulate the files using the DOM File API.

Example: Imagify
Guides: Using files from web applications | File drag and drop
API references: DOM File API

Store files data locally using the IndexedDB file storage library

If your extension needs to save files locally, the idb-file-storage library provides a simple Promise-based wrapper to the IndexedDB API to aid the storage and retrieval of files and blobs.

On Firefox, this library also provides a Promise-based API wrapper for the non-standard IDBMutableFile API. (The IDBMutableFile API enables extensions to create and persist an IndexedDB database file object that provides an API to read and change the file’s content without loading all the file into memory.)

The key features of the library are:

  • getFileStorage that returns an IDBFileStorage instance, creating the named storage if it does not exist.
  • IDBFileStorage that provides the methods to save and retrieve files, such as:
    • list to obtain an optionally filtered list of file in the database.
    • put to add a file or blob to the database.
    • get to retrieve a file or blob from the database.
    • remove to delete a file or blob from the database.

The Store Collected Images example illustrates how to use most of these features. (IDBMutableFile is not included, but you can find examples in the idb-file-storage examples along with a number of other examples of the library in action).

The Store Collected Images example lets users add images to a collection using an option on the image context menu. Selected images are collected in a popup and can be saved to a named collection. A toolbar button (browserAction) opens a navigate collection page, on which the user can view and delete saved images, with a filter option to narrow choices. See the example in action.

The workings of the library can be understood by viewing image-store.js in /utils/:

Creating the store and saving the images

async function saveCollectedBlobs(collectionName, collectedBlobs) {
 const storedImages = await getFileStorage({name: "stored-images"});
 for (const item of collectedBlobs) {
    await storedImages.put(`${collectionName}/${item.uuid}`, item.blob);
 }
}

saveCollectedBlobs is called when the user clicks save in the popup and has provided a name for the image collection. First, getFileStorage creates, if it does not exist already, or retrieves the IndexedDB database “stored-images” to the object storedImages. storedImages.put then adds each collected image to the database, under the collection name, using the blob’s unique id (the file name). If the image being store has the same name as one already in the database, it is overwritten. If you want to avoid this, query the database first using imagesStore.list() with a filter for the file name, and, if the list returns a file, add a suitable suffix to the name of the new image to store a separate item.

Retrieving stored images for display

export async function loadStoredImages(filter) {
 const imagesStore = await getFileStorage({name: "stored-images"});
 let listOptions = filter ? {includes: filter} : undefined;
 const imagesList = await imagesStore.list(listOptions);
 let storedImages = [];
 for (const storedName of imagesList) {
    const blob = await imagesStore.get(storedName);
    storedImages.push({storedName, blobUrl: URL.createObjectURL(blob)});
 }
 return storedImages;
}

loadStoredImages is called when the user clicks view or reload in the navigate collection page. getFileStorage opens the “stored-images” database, then imagesStore.list gets a filtered list of the stored images. This list is then used to retrieve images with imagesStore.get and build a list to return to the UI.

Note the use of URL.createObjectURL(blob) to create a URL that references the image blob. This URL is then used in the UI (navigate-collection.jscollection.js) to display the image.

Delete collected images

async function removeStoredImages(storedImages) {
 const imagesStore = await getFileStorage({name: "stored-images"});
 for (const storedImage of storedImages) {
    URL.revokeObjectURL(storedImage.blobUrl);
    await imagesStore.remove(storedImage.storedName);
 }
}

removeStoredImages is called when the user clicks delete in the navigate collection page. Again, getFileStorage opens the “stored-images” database then imagesStore.remove removes each image from the filtered list of images.

Note the use of URL.revokeObjectURL() to explicitly revoke the blob URL. This enables the garbage collector to free the memory allocated to the URL. If this is not done, the memory will not get returned until the page on which it was created is closed. If the URL was created in an extension’s background page, this is not unloaded until the extension is disabled, uninstalled, or reloaded, so holding this memory unnecessarily could affect browser performance. If the URL is created in an extension’s page (new tab, popup, or sidebar) the memory is released when the page is closed, but it is still a good practice to revoke the URL when it is no longer needed.

Once the blob URL has been revoked any attempt to load it will result in an error. For example, if the blob url was used as the SRC attribute of an IMG tag, the image will not load and will not be visible. It is therefore good practice to remove any revoked blob urls from generated HTML elements when the blob URL is revoked.

Example: Store Collected Images
API References:  idb-file-storage library

Note: You can also use the full Web IndexedDB API to store data from your extension. This can be useful where you need to store data that isn’t handled well by the simple key/value pairs offered by the DOM Storage API.

Process files in a local app

Where you have a native app or want to deliver additional native features for file processing, use native messaging to pass a file to a native app for processing.

You have two options:

  • Connection-based messaging. Here you trigger the process with runtime.connectNative(), which returns a runtime.Port object. You can then pass a JSON message to the native application using the postMessage() function of Port. Using the onMessage.addListener() function of Port you can listen for messages from the native application. The native application is opened if it is not running when runtime.connectNative() is called and the application remains running until the extension calls Port.disconnect() or the page that connected to it is closed.
  • Connectionless messaging. Here you use runtime.sendNativeMessage() to send a JSON message to a new, temporary instance of the native application. The browser closes the native application after receiving any message back from the native application.

To add the file or blob you want the native application to process use JSON.stringify().

To use this method the extension must request the "nativeMessaging" permission in its manifest.json file. Reciprocally, the native application must grant permission for the extension by including its ID in the "allowed_extensions" field of the app manifest.

Example: Native Messaging (illustrates simple messaging only)
Guides: Native messaging
API references: runtime API

Document Tags and Contributors

Tags: 
  • Guide
  • WebExtensions
 Contributors to this page: rebloor
 Last updated by: rebloor, Jul 21, 2017, 1:42:09 PM
See also
  1. Browser extensions
  2. Getting started
    1. What are extensions?
    2. Your first extension
    3. Your second extension
    4. Anatomy of an extension
    5. Example extensions
  3. How to
    1. Intercept HTTP requests
    2. Modify a web page
    3. Add a button to the toolbar
    4. Implement a settings page
  4. User interface
    1. Introduction
    2. Toolbar button
    3. Address bar button
    4. Sidebar
    5. Context menu items
    6. Options page
    7. Bundled web pages
    8. Notifications
    9. Address bar suggestions
    10. Developer tools panels
  5. Concepts
    1. Using the JavaScript APIs
    2. Content scripts
    3. Match patterns
    4. Internationalization
    5. Content Security Policy
    6. Native messaging
  6. Porting
    1. Porting a Google Chrome extension
    2. Porting a legacy Firefox extension
    3. Embedded WebExtensions
    4. Comparison with the Add-on SDK
    5. Comparison with XUL/XPCOM extensions
    6. Chrome incompatibilities
    7. Differences between desktop and Android
  7. Firefox workflow
    1. Temporary Installation in Firefox
    2. Debugging
    3. Developing for Firefox for Android
    4. Getting started with web-ext
    5. web-ext command reference
    6. Extensions and the Add-on ID
    7. Publishing your extension
  8. JavaScript APIs
    1. Browser support for JavaScript APIs
    2. alarms
    3. bookmarks
    4. browserAction
    5. browsingData
    6. commands
    7. contextMenus
    8. contextualIdentities
    9. cookies
    10. devtools.inspectedWindow
    11. devtools.network
    12. devtools.panels
    13. downloads
    14. events
    15. extension
    16. extensionTypes
    17. history
    18. i18n
    19. identity
    20. idle
    21. management
    22. notifications
    23. omnibox
    24. pageAction
    25. permissions
    26. privacy
    27. proxy
    28. runtime
    29. sessions
    30. sidebarAction
    31. storage
    32. tabs
    33. topSites
    34. types
    35. webNavigation
    36. webRequest
    37. windows
  9. Manifest keys
    1. applications
    2. author
    3. background
    4. browser_action
    5. chrome_settings_overrides
    6. chrome_url_overrides
    7. commands
    8. content_scripts
    9. content_security_policy
    10. default_locale
    11. description
    12. developer
    13. devtools_page
    14. homepage_url
    15. icons
    16. incognito
    17. manifest_version
    18. name
    19. omnibox
    20. optional_permissions
    21. options_ui
    22. page_action
    23. permissions
    24. protocol_handlers
    25. short_name
    26. sidebar_action
    27. version
    28. web_accessible_resources
  10. Themes
  11. Publishing add-ons
  12. Guides
    1. Signing and distribution overview
    2. Submit an add-on
    3. Creating an appealing listing
    4. Review policies
    5. Developer agreement
    6. Featured add-ons
    7. Contact addons.mozilla.org
  13. Community and support
  14. Channels
    1. Add-ons blog
    2. Add-on forums
    3. Stack Overflow
    4. Development newsgroup
    5. IRC Channel
  15. Legacy add-ons
  16. Legacy technologies
    1. Add-on SDK
    2. Legacy Firefox for Android
    3. Bootstrapped extensions
    4. Overlay extensions