In [180]:
from __future__ import print_function # py 2.7 compat.

import base64
import urllib

# Widget definitions.
from IPython.html import widgets

# Traitlet needed to add synced attributes to the widget.
from IPython.utils.traitlets import Unicode

This is a custom widget that allows the user to upload file data to the notebook server. The file data is sent via a statefull value attribute of the widget. The widget has an upload failed event that fires in the front-end and is echoed to the back-end using a custom msg.

In [181]:
class FileWidget(widgets.DOMWidget):
    _view_name = Unicode('FilePickerView', sync=True)
    value = Unicode(sync=True)
    filename = Unicode(sync=True)
    def __init__(self, **kwargs):
        widgets.DOMWidget.__init__(self, **kwargs) # Call the base.
        # Allow the user to register error callbacks with the following signatures:
        #    callback()
        #    callback(sender)
        self.errors = widgets.CallbackDispatcher(accepted_nargs=[0, 1])
        # Listen for custom msgs

    def _handle_custom_msg(self, content):
        """Handle a msg from the front-end.

        content: dict
            Content of the msg."""
        if 'event' in content and content['event'] == 'error':
In [182]:

["jquery", "underscore", "widgets/js/manager"],

function($, _, wm, Dropzone){    
    var FilePickerView = IPython.DOMWidgetView.extend({
        render: function(){
            // Render the view.
            this.$el.append($("<input/>", {type: "file"}));
        events: {
            change: "handle_file_change"
        handle_file_change: function(evt) { 
            // Handle when the user has changed the file.
            // Retrieve the first (and only!) File from the FileList object
            var file =[0];
            if (file) {
                // Read the file's textual content and set value to those contents.
                var that = this;
                var file_reader = new FileReader();
                file_reader.onload = function(e) {
                // by using a `data:` url, we get a nice portable representation which can be
                // easily rendered or embedded.
            } else {
                // The file couldn't be opened.  Send an error msg to the
                // back-end.
                this.send({event: 'error'});

            // Set the filename of the file.
    // Register the DatePickerView with the widget manager.
    wm.WidgetManager.register_widget_view('FilePickerView', FilePickerView);

Once the data url gets back to the backend, we'll want to do some work against it: this gist provides this nice API.

In [183]:
import mimetypes
import re
import urllib
MIMETYPE_REGEX = r'[\w]+\/[\w\-\+\.]+'
_MIMETYPE_RE = re.compile('^{}$'.format(MIMETYPE_REGEX))
CHARSET_REGEX = r'[\w\-\+\.]+'
_CHARSET_RE = re.compile('^{}$'.format(CHARSET_REGEX))
    r'data:' +
    r'(?P<mimetype>{})?'.format(MIMETYPE_REGEX) +
    r'(?:\;charset\=(?P<charset>{}))?'.format(CHARSET_REGEX) +
    r'(?P<base64>\;base64)?' +
_DATA_URI_RE = re.compile(r'^{}$'.format(DATA_URI_REGEX), re.DOTALL)
class DataURI(str):
    def make(cls, mimetype, charset, base64, data):
        parts = ['data:']
        if mimetype is not None:
            if not _MIMETYPE_RE.match(mimetype):
                raise ValueError("Invalid mimetype: %r" % mimetype)
        if charset is not None:
            if not _CHARSET_RE.match(charset):
                raise ValueError("Invalid charset: %r" % charset)
            parts.extend([';charset=', charset])
        if base64:
            encoded_data = data.encode('base64').replace('\n', '')
            encoded_data = urllib.quote(data)
        parts.extend([',', encoded_data])
        return cls(''.join(parts))
    def from_file(cls, filename, charset=None, base64=True):
        mimetype, _ = mimetypes.guess_type(filename, strict=False)
        with open(filename) as fp:
            data =
        return cls.make(mimetype, charset, base64, data)
    def __new__(cls, *args, **kwargs):
        uri = super(DataURI, cls).__new__(cls, *args, **kwargs)
        uri._parse  # Trigger any ValueErrors on instantiation.
        return uri
    def __repr__(self):
        return 'DataURI(%s)' % (super(DataURI, self).__repr__(),)
    def wrap(self, width=76):
        return type(self)('\n'.join(textwrap.wrap(self, width)))
    def mimetype(self):
        return self._parse[0]
    def charset(self):
        return self._parse[1]
    def is_base64(self):
        return self._parse[2]
    def data(self):
        return self._parse[3]
    def _parse(self):
        match = _DATA_URI_RE.match(self)
        if not match:
            raise ValueError("Not a valid data URI: %r" % self)
        mimetype ='mimetype') or None
        charset ='charset') or None
            data ='data').decode('base64')
            data = urllib.unquote('data'))
        return mimetype, charset, bool('base64')), data

The following shows how the file widget can be used.

In [184]:
import struct
file_widget = FileWidget()

# Register an event to echo the filename when it has been changed.
def file_loading():
    print("Loading %s" % file_widget.filename)
file_widget.on_trait_change(file_loading, 'filename')

# Register an event to echo the filename and contents when a file
# has been uploaded.
def file_loaded():
    uri = DataURI(file_widget.value)
    with open(file_widget.filename, "w+") as f:
    print("Loaded, file contents saved to: %s" % file_widget.filename)
file_widget.on_trait_change(file_loaded, 'value')

# Register an event to print an error message when a file could not
# be opened.  Since the error messages are not handled through
# traitlets but instead handled through custom msgs, the registration
# of the handler is different than the two examples above.  Instead
# the API provided by the CallbackDispatcher must be used.
def file_failed():
    print("Could not load file contents of %s" % file_widget.filename)

Loading thumbnail.png
Loaded, file contents saved to: thumbnail.png