Le blog

Uploader des gros fichiers dans WordPress avec Plupload

Dans le cadre d’une refonte de site internet avec WordPress, un de nos clients avait besoin d’un développement sur mesure : les rédacteurs du site doivent pouvoir régulièrement uploader des fichiers assez lourds (zip de plus de 50Mo) dans des custom posts.

Dans sa version précédente (non Wordpress), le site internet utilisait une applet Java mais celle ci avait plusieurs inconvénients (présence de Java sur le poste de l’auteur, fonctionnalité régulièrement bloquée par des mises à jour…). Il fallait également tenir compte des limitations de WordPress et de l’hébergement en termes de tailles de fichiers (upload_max_filesize et post_max_size). Qui ne sont pas toujours configurables, en particulier sur des mutualisés.

La solution semblait un peu compliquée à trouver mais elle existe 🙂 Depuis sa version 3.3, WordPress embarque la librairie d’upload Plupload et partant de cela, j’ai trouvé un petit tuto en anglais pour l’utiliser (ici pour ceux que ça intéresse). Mais cela ne répondait pas totalement  à notre problématique car les fichiers de notre client étaient beaucoup trop. Heureusement, Plupload inclue une option pour envoyer les fichiers par paquets (chunk). Bon il a fallu un peu ruser mais au final ça fonctionne bien, et sans module supplémentaire. Let’s go !

Appel des scripts JS et des feuilles de la CSS

[pastacode lang= »php » message= » » highlight= » » provider= »manual »]

[/pastacode]

Tout d'abord, nous allons utiliser le hook admin_enqueue_scripts pour inclure nos scripts et styles. Nous ajoutons également une condition pour éviter le chargement de ces éléments en dehors du contexte où ils seront utilisés.
Les scripts chargés sont le javascript Pupload du coeur de WordPress et nos scripts spécifiques : myupload.js et myupload.css.

Insertion de données JS via le hook admin_head

[pastacode lang="php" message="" highlight="" provider="manual"]

 'html5,silverlight,html4',
        'browse_button' => 'plupload-browse-button',
        'container' => 'plupload-upload-ui',
        'drop_element' => 'dropzone',
        'file_data_name' => 'async-upload',
        'chunk_size' => '9mb', // taille maximum de chaque paquet
        'url' => admin_url( 'admin-ajax.php' ),
        'silverlight_xap_url' => includes_url( 'js/plupload/plupload.silverlight.xap' ),
        'filters' => array( array( 'title' => __( 'Allowed Files' ), 'extensions' => 'zip' ) ),
        'urlstream_upload' => true,
        'multi_selection' => false,
        // données post supplémentaires à envoyé à notre hook ajax
        'multipart_params' => array(
            '_ajax_nonce' => "", // sera ajouté par l'uploader
            'action' => 'plupload_action',
            'file' => 0 // sera ajouté par l'uploader
        )
    );
?>
  

[/pastacode]

Ces éléments de configuration seront utilisés par l'uploader. Pour plus d'infos sur les paramètres disponibles vous pouvez aller sur la doc de Plupload (http://www.plupload.com/docs/).
Dans notre cas le paramètre important est chunk_size qui va définir la taille maximum de chaque paquet. Si le fichier envoyé est plus grand il sera éclaté.

Ajout du HTML

[pastacode lang="php" message="" highlight="" provider="manual"]
















Import

[/pastacode]

Pour la refonte WordPress du site de notre client, nous avons inséré ce code sur une page spécifique pour l'import dans le back-office. Dans un souci d'homogénéité avec le fonctionnement de WordPress pour l'upload de médias, nous avons mis une balise div pour une zone de drag and drop, qui contient également un bouton pour directement parcourir sur le poste de l'utilisateur. Les identifiants font directement au paramètres Plupload définis précédemment.

Handler pour l'envoi du fichier en Ajax

[pastacode lang="php" message="" highlight="" provider="manual"]

 false, 'action' => 'plupload_action' ) );
        
    // Construction du fichier définitif au fur et à mesure de la réception des paquets
    $out = @fopen( $upload_dir['path'].'/'.$filetmp, $chunk == 0 ? "wb" : "ab");
    if ($out) :
        $in = @fopen( $upload_dir['path'].'/'.$filepart, "rb");

        if ($in) :
            while ($buff = fread($in, 4096))
                fwrite($out, $buff);
        else :
          die('{"OK": 0, "info": "Failed to open input stream."}');
        endif;
        @fclose($in);
        @fclose($out);
        @unlink($upload_dir['path'].'/'.$filepart);
    else :
        die('{"OK": 0, "info": "Failed to open output stream."}');
    endif;

    // Renommage du fichier avec son nom d'origine
    if (!$chunks || $chunk == $chunks - 1) :
        rename( $upload_dir['path'] . '/' . $filetmp, $upload_dir['path'] . '/' . $_REQUEST["name"]);
    endif;
        
    // Renvoi du chemin du fichier uploadé
    echo $upload_dir['path'] . '/' . $_REQUEST["name"];
    exit;
}
add_action( 'wp_ajax_plupload_action', 'plupload_action' );  

[/pastacode]

Bon voilà le gros morceau. Outre les vérifications classiques (fichier existant, nonce...), nous avons changé le répertoire de destination pour ne pas polluer le répertoire uploads de WordPress.
Nous valorisons ensuite une variable afin de tester plus tard si l'upload courant est le fichier intégral ou une partie.
Point important : pour chaque paquet, Plupload génère un fichier blob sans extension. WordPress n'accepte pas ce type d'upload. Pour contourner cette difficulté, nous avons renommé le fichier en blob.part et autorisé ce type mime avec le filtre upload_mimes.
Nous utilisons ensuite le système de fichiers PHP pour additionner les différentes parties du fichier au fur et à mesure de l'upload.
Enfin, nous renommons le fichier avec le nom de fichier d'origine.

myupload.js

[pastacode lang="javascript" message="" highlight="" provider="manual"]

jQuery.fn.exists = function() {  
    return jQuery(this).length > 0;
}
jQuery(document).ready(function($) {

    if ($(".plupload-upload-uic").exists()) {
        var pconfig = false;
        $(".plupload-upload-uic").each(function() {
            var $this = $(this);
            var id1 = $this.attr("id");
            var fileId = id1.replace("plupload-upload-ui", "");

            pconfig = JSON.parse(JSON.stringify(base_plupload_config));

            pconfig["browse_button"] = fileId + pconfig["browse_button"];
            pconfig["container"] = fileId + pconfig["container"];
            pconfig["drop_element"] = fileId + pconfig["drop_element"];
            pconfig["file_data_name"] = fileId + pconfig["file_data_name"];
            pconfig["multipart_params"]["fileid"] = fileId;
            pconfig["multipart_params"]["_ajax_nonce"] = $this.find(".ajaxnonceplu").attr("id").replace("ajaxnonceplu", "");

            var uploader = new plupload.Uploader(pconfig);

            uploader.bind('Init', function(up) {
            });

            uploader.init();

            // Fichier ajouté à la file
            uploader.bind('FilesAdded', function(up, files) {
                $.each(files, function(i, file) {
                    $this.find('.filelist').append('














' + file.name + ' (' + plupload.formatSize(0) + '/' + plupload.formatSize(file.size) + ') ' + '
'); }); up.refresh(); up.start(); }); uploader.bind('UploadProgress', function(up, file) { $('#' + file.id + " .fileprogress").width(file.percent + "%"); $('#' + file.id + " span").html(plupload.formatSize(parseInt(file.size * file.percent / 100))); }); uploader.bind("ChunkUploaded", function(up, file, response){ // Retroune un code 200 si l'upload est correct if(response.status != null && response.response != null) { if(response.status != "200" || (response.response.indexOf("Error") != -1)) { if(response.response.indexOf("Error") != -1) { $("div.error").show().html(' '+response.response+' '); } else { // Notifie l'erreur $("div.error").show().html(' There was an error uploading your file '+ file.name +' Support has been notified. '); } $('#' + file.id).addClass("cancelled"); uploader.stop(); } } }); // Fichier uploadé uploader.bind('FileUploaded', function(up, file, response) { $('#zip_filedropzone').fadeOut(); $('#' + file.id).fadeOut(); $('#import_progress').fadeIn(); zip_path = response["response"]; if ( response !== '' ) { $.ajax({ type: 'POST', url: my_ajaxurl, data: { action: "my_unzip", file_path: zip_path }, success: function( response ) { // Traitement spécifique à effectuer une fois le fichier importé } }); } }); }); } // Définition de la zone de drag & drop et des événements associés var droppable = $('#zip_filedropzone'), lastenter; droppable.on("dragenter", function (event) { lastenter = event.target; droppable.addClass("drag-over"); }); droppable.on("dragleave", function (event) { if (lastenter === event.target) { droppable.removeClass("drag-over"); } }); droppable.on("drop", function (event) { droppable.removeClass("drag-over"); }); });

[/pastacode]

Autre morceau important : le traitement JavaScript. Ici nous initialisons l'uploader et utilisons ses différents événements pour traiter la barre progression et traiter le fichier reçu. Là aussi vous pouvez vous reporter à la documentation de Plupload pour les événements disponibles.

myupload.css

[pastacode lang="css" message="" highlight="" provider="manual"]

.filelist {
    width: 60%;
    margin:80px auto 0;
}
.filelist .file {
    padding: 5px;
    background: #ececec;
    border: solid 1px #ccc;
    margin-bottom: 4px;
}
.filelist .fileprogress {
    width: 0%;
    background: #0073AA;
    height: 5px;
    margin-top:5px;
}
.dropzone{
    height:200px;
    text-align:center;
    border:4px dashed #B4B9BE;
}
.dropzone.drag-over{
    border-color:#83b4d8;
}
.uploader{
    margin:50px auto 0;
}
.plupload-upload-uic input.button{
    height:46px;
    line-height:44px;
    padding:0 36px;
    font-size:14px;
}
.uploader p{
    font-size:12px;
}
#import_progress{
    text-align:center;
    display:none;
}

[/pastacode]

On termine léger avec la CSS utilisée.

Voilà, libre à vous de personnaliser selon vos besoins, en utilisant par exemple la fonctionnalité de multi-upload présente nativement dans Plupload ou, ce qui a été notre cas, en effectuant un traitement à la fin de l'upload.

Enfin et bien sûr, nous restons à votre disposition pour toute autre info sur la création de sites avec WordPress.

Publié dans Création d'un blog ou site Wordpress
0da3306fd258c71128587daff2d9b03fk