Ajax Upload Photos via HTML5 File Reader API : #BuddyPress Checkins teaser !

Publié le

par

Crédits Photo : The Old Republic de Ryan Grove, sur Flickr

Salut,
Je suis à fond sur la prochaine version de BP Checkins. Dans les commentaires de mon article d’introduction de ce plugin BuddyPress, il m’a notamment été demandé de gérer les uploads d’images pour illustrer un checkin. Il me reste pas mal de taf sur cette nouvelle version, mais je me suis dit que le petit trick que j’utilise pour transférer des photos en Ajax pouvait vous intéresser et serait un bon teaser pour vous faire patienter 😉 https://vimeo.com/43250056

La démo ci-dessus, dans laquelle je parle anglais (vous avez le droit de me chambrer !! / you can laugh at my « frenglish » accent !!), illustre le fonctionnement du trick.

En fait, j’utilise la HTML5 File Reader API pour récupérer l’image encodée en base64 avant de l’envoyer en AJAX à mon script PHP. Dans ce script, je décode puis écris à la fois le fichier full size et son thumbnail dans le répertoire des uploads de WordPress. Astucieux non ? La seule ombre au tableau est que vous devez vous assurer de que le memory_size de votre php.ini est d’au moins 64M..

Côté client

Voici un extrait de l’html et du javascript que j’utilise dans BP Checkins que vous pouvez adapter selon vos besoins

/***** html *****/
<span id="form-upcheckin">
	<input type="file" name="_checkin_pic" id="checkin_pic">
</span>
<a href="#" id="bpci-polaroid-upload" title="Attach image">
	<span>Attach image</span>
</a>

/***** javascript *****/
$('#bpci-polaroid-upload').click( function(){
	var file = $('#checkin_pic').get(0).files[0];

	//size in MO
	var max_file_size = 2;

	// check file type, we only want images */
	if ( file.type.indexOf( 'image' ) == -1 ) {
		alert( 'Please select an image file only.' );
		return false;
	}

	var name = file.name, size = file.size / 1024, type = file.type,
		sizeInMo = Math.round((size / 1024) * 100) / 100;

	// check file size
	if( sizeInMo > max_file_size ) {
		alert( 'Your image is too big, please reduce it (max file size : ' + max_file_size + 'MO)' );
		return false;
	}

	var reader = new FileReader();
	$('#bpci-polaroid-upload').addClass('bpci-loading');

	reader.onload = (function(theFile) {
		return function(e) {
			bpciImageUpload(e.target.result, type, name);
			return false;
		};
	} )(file);

	reader.readAsDataURL(file);

	return false;
} );

function bpciImageUpload( img, type, name ) {
	/**
	 * ajaxurl is already defined in BuddyPress
	 * if you're not using BuddyPress, you can define it this way :
	 * ajaxurl = "<?php echo admin_url('admin-ajax.php');?>";
	 */
	$.post( ajaxurl, {
		action: 'upload_checkin_pic',
		'_wpnonce_post_checkin': $("input#_wpnonce_post_checkin").val(), //security in bp-checkins
		encodedimg: img,
		imgtype:type,
		imgname':name
	}, function(response) {
		if( response[0] != "0" ) {
			sendToContentEditable( response[1], response[2] );
		} else {
			alert(response[1]);
		}
	}, 'json');
}

function sendToContentEditable( fullimage, resizedimage ) {
	alert('full image url: '+fullimage+' / thumbnail url: '+ resizedimage);
}

Pour en savoir plus sur les possibilités de la Html5 File / File Reader API, je vous invite à consulter cet article de html5rocks qui m’a beaucoup inspiré 😉

Côté serveur

Ici, on profite de la fonction php base64_decode() pour décoder et écrire le fichier au sein du répertoire des uploads de WordPress. En passant, on s’assure de l’unicité du nom de fichier grâce à la fonction wp_unique_filename() de WP. Avant de renvoyer la réponse au client, on en profite pour écrire le cas échéant un thumbnail de cette image

/***** php *****/
function bp_checkins_handle_checkin_upload() {
	if( $_POST['encodedimg'] ) {
		check_admin_referer( 'post_checkin', '_wpnonce_post_checkin' ); // bp-checkins security

		$imgresponse = array();
		$uploaddir = wp_upload_dir();

		// let's decode the base64 encoded image sent
		$img = $_POST['encodedimg'];
		$img = str_replace('data:'.$_POST['imgtype'].';base64,', '', $img);
		$img = str_replace(' ', '+', $img);
		$data = base64_decode($img);
		$imgname = wp_unique_filename( $uploaddir['path'], $_POST['imgname'] );

		$filepath = $uploaddir['path'] . '/' . $imgname;
		$fileurl = $uploaddir['url'] . '/' . $imgname;

		// now we write the image in dir */
		$success = file_put_contents($filepath, $data);

		if ( $success ){
			$imgresponse[0] = "1";
			$imgresponse[1] = $fileurl;
			$size = @getimagesize( $filepath );

			// Check image size and shrink if too large
			if ( $size[0] > 100 ) {
				$thumb = image_resize( $filepath, 100, 100, true );

				// Check for thumbnail creation errors
				if ( is_wp_error( $thumb ) ) {
					$imgresponse[0] = "0";
					$imgresponse[1] = sprintf( __( 'Upload Failed! Error was: %s', 'bp-checkins' ), $thumb->get_error_message() );
					exit();
				}

				//Thumbnail is good so proceed
				$array_filepath = explode('/', $thumb);
				$image_resized = $array_filepath[count($array_filepath)-1];

				if ( !empty( $image_resized ) ) {
					$imgresponse[2] = $uploaddir['url'] . '/'. $image_resized;
				}
			}
		} else {
			$imgresponse[0] = "0";
			$imgresponse[1] = __('Upload Failed! Unable to write the image on server', 'bp-checkins');
		}
	} else {
		$imgresponse[0] = "0";
		$imgresponse[1] = __('Upload Failed! No image sent', 'bp-checkins');
	}

	// if everything is ok, we send back url to thumbnail and to full image 
	echo json_encode( $imgresponse );
	die();
}
add_action( 'wp_ajax_upload_checkin_pic', 'bp_checkins_handle_checkin_upload' );

Merci à Jamund pour son php trick que je me suis permis d’adapter à mon besoin

Conclusion

Dans mon plugin, avant de rendre dispo cette fonctionnalité, je vérifierai côté serveur si la valeur du php.ini pour la memory_size est bien de 64M et côté client si le navigateur prend en charge la File Reader API. Autrement, je mets à disposition un champ pour coller l’url vers l’image…

What’s next ? Comme annoncé en début de cet article, je travaille d’arrache pied pour terminer au plus tôt cette nouvelle version. Je m’amuse comme un petit fou avec différentes classes et fonctions BuddyPress / WordPress. Du coup, le prochain article de ce blog sera la deuxième partie de ma série « Etendre BuddyPress » dans laquelle je décrirai comment concevoir un composant BuddyPress en m’appuyant sur l’exemple de BP Checkins 😉

Many Thanks to the BuddyPress Community for the feedbacks, ratings and interest in this plugin 🙂

7 réponses à “Ajax Upload Photos via HTML5 File Reader API : #BuddyPress Checkins teaser !”

  1. Avatar de Selu VEga

    simply rocks!

    Thanks, 😉

    1. Avatar de imath

      Thanks for your feedback @ Selu :))

  2. Avatar de JD

    Top shelf plugin – perfect for what I am looking to do.

    1. Avatar de imath

      Thanks for your feedback 🙂

  3. Avatar de Philippe Gras
    Philippe Gras

    C’est très intéressant. Même si je ne comprends pas à quoi ça sert, je vais m’en servir pour télécharger des fichiers.

  4. Avatar de spade5702
    spade5702

    I’ve been waiting for a plugin like this for years. You rock! By the way, whens the next update going to be ready? I’m very excited about the next update.

  5. Avatar de imath

    Hi spade5702,

    Thanks for your comment, i think i’ll publish a beta of next version in a few days.

    😉