3

I'm trying to build a webcam snapshot recorder. It should take a picture and upload it to the WP media library via ajax / async-upload.

Already managed to add a view to the media library. Recording also works. I can either store the raw image data in blob or in base 64 encoded string (like data url)

How can I put this data into the upload queue?

Any suggestion / further reading / well explained reasons why this won't work appreciated.

Thanks a lot, joern

EDIT: This is my code so far. It

  • adds a panel to WP media library.

  • displays an image with src="data:image/

  • Tries to generate a file-like object and put it in the upload queue. (That's where I'm failing)

JS-Dependency: Needs github repo blueimp/JavaScript-Canvas-to-Blob

<script type="text/javascript">
(function($){
var media = wp.media,
    l10n = media.view.l10n = typeof _wpMediaViewsL10n === 'undefined' ? {} : _wpMediaViewsL10n;

// override router creation
media.view.MediaFrame.Post.prototype.browseRouter = function( view ) {
    view.set({
        upload: {
            text:     l10n.uploadFilesTitle,
            priority: 20
        },
        dataurltest: {
            text:     'DataUrl Test', 
            priority: 30
        },
        browse: {
            text:     l10n.mediaLibraryTitle,
            priority: 40
        }
    });
};

var bindHandlers = media.view.MediaFrame.Post.prototype.bindHandlers,
    dataUrlTest, frame;

media.view.MediaFrame.Post.prototype.bindHandlers = function() {
    // bind parent object handlers
    bindHandlers.apply( this, arguments );
    // bind our create handler.
    this.on( 'content:create:dataurltest', this.dataurltestContent, this );
    frame = this;
};
media.view.MediaFrame.Post.prototype.dataurltestContent = function( content ){
    // generate test content
    var state = this.state();
    this.$el.removeClass('hide-toolbar');
    dataUrlTest = new media.view.dataUrlTest({});
    content.view = dataUrlTest;
}


media.view.dataUrlTest = media.View.extend({
    // our test view
    tagName:   'div',
    className: 'data-url-test',

    initialize: function() {
        _.defaults( this.options, {

        });
        var self = this, 
            blob;


        // add image with data-url to panel content
        var b64Data = 'R0lGODdhUAA8AIABAAAAAP///ywAAAAAUAA8AAACS4SPqcvtD6' +
                'OctNqLs968+w+G4kiW5omm6sq27gvH8kzX9o3n+s73/g8MCofE' +
                'ovGITCqXzKbzCY1Kp9Sq9YrNarfcrvcLDovH5PKsAAA7',
            imageUrl = 'data:image/gif;base64,' + b64Data,
            $img = $('<img src="'+imageUrl+'" />')
                .appendTo(this.$el);
        // add a submit link
        $('<a href="#" class="test-send">Okay</a>')
            .insertAfter($img)
            .on('click',null,function(){
                // creating a blob would go like this:
                //blob = window.dataURLtoBlob && window.dataURLtoBlob($img.get(0).src)

                var file = new plupload.File(null,b64Data);
                file.name = 'test.gif';
                file.id = 'id'+(new Date()).getTime(); 

                var attributes = _.extend({
                    file:      file,
                    uploading: true,
                    date:      new Date(),
                    filename:  'test.gif',
                    menuOrder: 0,
                    uploadedTo: wp.media.model.settings.post.id,
                    type : 'image',
                    subtype : 'gif'
                }, _.pick( file, 'loaded', 'size', 'percent' ) );

                file.attachment = wp.media.model.Attachment.create( attributes );
                wp.Uploader.queue.add(file.attachment);
                frame.uploader.uploader.uploader.refresh();
                frame.uploader.uploader.uploader.start();
            });

    },
});

return;




})(jQuery);
</script>
2
  • Are the files already on an FTP source somewhere? – GhostToast Jan 16 '14 at 22:03
  • Could you please not link to outside sources but put the code (in a to the relevant parts reduced form) in the question? Prevents link rot. Thanks. – kaiser Jan 16 '14 at 22:17
5

Finally I found a solution on my own.

There is no direct way to pass pure binary data (like a blob) to the WP upload object. Only File based objects will be processed. I had to create a separate XmlHttpRequest. (Simply using $.ajax() did not work and resulted in polluted Image data.)

After successfully uploading the image data to async-upload.php I was able to fire a 'FileUploaded' event, along with what the server responded. After this the image showd up in the media library.

The code example above could look like this (untested and still sketchy):

<script type="text/javascript">
(function($){
var media = wp.media,
    frame,
    l10n = media.view.l10n = typeof _wpMediaViewsL10n === 'undefined' ? {} : _wpMediaViewsL10n;

// override router creation
media.view.MediaFrame.Post.prototype.browseRouter = function( view ) {
    view.set({
        upload: {
            text:     l10n.uploadFilesTitle,
            priority: 20
        },
        dataurltest: {
            text:     'DataUrl Test', 
            priority: 30
        },
        browse: {
            text:     l10n.mediaLibraryTitle,
            priority: 40
        }
    });
};

var bindHandlers = media.view.MediaFrame.Post.prototype.bindHandlers,
    dataUrlTest, frame;

media.view.MediaFrame.Post.prototype.bindHandlers = function() {
    // bind parent object handlers
    bindHandlers.apply( this, arguments );
    // bind our create handler.
    this.on( 'content:create:dataurltest', this.dataurltestContent, this );
    frame = this;
};
media.view.MediaFrame.Post.prototype.dataurltestContent = function( content ){
    // generate test content
    var state = this.state();
    this.$el.removeClass('hide-toolbar');
    dataUrlTest = new media.view.dataUrlTest({});
    content.view = dataUrlTest;
}


media.view.dataUrlTest = media.View.extend({
    // our test view
    tagName:   'div',
    className: 'data-url-test',

    initialize: function() {
        _.defaults( this.options, {

        });
        var self = this, 
            blob;

        // add image with data-url to panel content
        var b64data =   'iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAGXRFWHRTb2Z0d2F'+
                        'yZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bX'+
                        'AAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTe'+
                        'k5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8i'+
                        'IHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjA'+
                        'xMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPS'+
                        'JodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gP'+
                        'HJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8v'+
                        'bnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmF'+
                        'kb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG'+
                        '9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yV'+
                        'G9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5z'+
                        'dGFuY2VJRD0ieG1wLmlpZDpGN0NENTMwNzdCNzcxMUUzQTBFQzg3RURFQTJCNTM'+
                        '5QiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpGN0NENTMwODdCNzcxMUUzQT'+
                        'BFQzg3RURFQTJCNTM5QiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0Y'+
                        'W5jZUlEPSJ4bXAuaWlkOkY3Q0Q1MzA1N0I3NzExRTNBMEVDODdFREVBMkI1MzlC'+
                        'IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkY3Q0Q1MzA2N0I3NzExRTNBMEV'+
                        'DODdFREVBMkI1MzlCIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+ID'+
                        'wveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+wqqcoQAAAAZQTFRFAAAA/'+
                        '///pdmf3QAAABFJREFUeNpiYBgFo4D6ACDAAAJYAAFvc4UPAAAAAElFTkSuQmCC',
            imageUrl = 'data:image/png;base64,' + b64data,
            $img = $('<img src="'+imageUrl+'" />')
                .appendTo(this.$el);
        // add a submit link
        $('<a href="#" class="test-send">Okay</a>')
            .insertAfter($img)
            .on('click',null,function(){
                // creating a blob would go like this:
                //blob = window.dataURLtoBlob && window.dataURLtoBlob($img.get(0).src)

                // create and add item to upload queue
                var file = {}; 
                var attributes = {
                    file:      file,
                    uploading: true,
                    date:      new Date(),
                    filename:  'test.png',
                    menuOrder: 0,
                    uploadedTo: wp.media.model.settings.post.id,
                    type : 'image',
                    subtype : 'png',
                    loaded : 0,
                    size : 100,
                    percent : 0
                };

                file.attachment = wp.media.model.Attachment.create( attributes );
                wp.Uploader.queue.add(file.attachment);
            
                var post_data = {
                    action   : wp.Uploader.defaults.multipart_params.action,
                    _wpnonce : wp.Uploader.defaults.multipart_params._wpnonce,
                    post_id  : wp.media.model.settings.post.id
                }
            
                 function upload_succes( xhr , httpStatus ) {
                    // this will update the uploaded image in the media library
                    frame.uploader.uploader.uploader.trigger('FileUploaded', file, {
                        response : xhr.responseText,
                        status : httpStatus
                    });
                }
           
                send_b64_data( b64data , attributes.filename , post_data , upload_succes );
            });

    },
});

return;




})(jQuery);
</script>

The function send_b64_data( b64data , filename , post_data , success_callback ) would look like this:

function send_b64_data( b64data , filename , post_data , success_callback ) {

    var boundary, dashdash, crlf, multipart_string,
        file_data_name, name, mime_type , xhr;
    
    // setup multipart
    boundary            = '----multipart_boundary'+(new Date().getTime().toString(32))+Math.random().toString(32);
    dashdash            = '--';
    crlf                = '\r\n';
    
    
    // build request payload
    multipart_string    = '';
    for ( name in post_data ) {
        multipart_string += dashdash + boundary + crlf +
            'Content-Disposition: form-data; name="' + name + '"' + crlf + crlf;
        multipart_string += unescape(encodeURIComponent(post_data[name])) + crlf;
    }
    
    // add image data
    mime_type       = 'image/png';
    file_data_name  = 'async-upload';
    multipart_string += dashdash + boundary + crlf +
        'Content-Disposition: form-data; name="' + wp.Uploader.defaults.file_data_name + '"; filename="' + filename + '"' + crlf +
        'Content-Type: ' + mime_type + crlf +
            crlf + atob( b64data ) + crlf +
            dashdash + boundary + dashdash + crlf;
    
    // build and send request
    xhr = new XMLHttpRequest()
    xhr.open("post", wp.Uploader.defaults.url, true);
    xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
    xhr.onreadystatechange = function() {
        var httpStatus, chunkArgs;
        if (xhr.readyState == 4 ) {
            try {
                httpStatus = xhr.status;
            } catch (ex) {
                httpStatus = 0;
            }
            if (httpStatus == 200) {
                // will load contents to file fake
                success_callback(xhr,httpStatus);
            } else if ( httpStatus >= 400 ) {
                // handle error
            }
        }
    }
    
    if (xhr.sendAsBinary) { // Gecko
        xhr.sendAsBinary(multipart_string);
    } else { // WebKit with typed arrays support
        var ui8a = new Uint8Array(multipart_string.length);
        for (var i = 0; i < multipart_string.length; i++) {
            ui8a[i] = (multipart_string.charCodeAt(i) & 0xff);
        }
        xhr.send(ui8a.buffer);
    }
}

Working code can be found here: https://github.com/mcguffin/say-cheese (WP Plugin allowing you to add webcam snapshots and pasted images to the MediaLibrary)

1
  • thanks for posting your plugin, the "paste from clipboard"-functionality is exactly what I have been looking for! – user2015 Dec 27 '14 at 8:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.