Cargador de archivos de Dropbox con Twitter Bootstrap

Hace unas semanas, Dropbox presentó una nueva característica interesante:el Selector de Dropbox. Al incorporarlo en su sitio web, les da a los usuarios un botón con el que pueden adjuntar archivos desde su almacenamiento en línea.

Hoy vamos a utilizar esta función para crear una aplicación sencilla que permita a las personas adjuntar una foto desde su cuenta de Dropbox, recortar un área con el complemento Jcrop y descargar el resultado. Además, utilizaremos Twitter Bootstrap para mostrar ventanas de diálogo y PHP en el backend para el recorte de fotos real.

El HTML

Para empezar, aquí está el documento HTML con el que trabajaremos. En la cabeza, incluyo las hojas de estilo para bootstrap, el complemento Jcrop y nuestro archivo CSS personalizado. En el pie de página, tenemos la biblioteca JavaScript de Dropbox, jQuery, Bootstrap, Jcrop y script.js , que estaremos escribiendo en la siguiente sección.

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Dropbox File Uploader With Twitter Bootstrap | Tutorialzine </title>

        <!-- The stylesheets -->
        <link rel="stylesheet" href="assets/css/bootstrap.min.css"  />
        <link rel="stylesheet" href="assets/Jcrop/jquery.Jcrop.min.css" />
        <link rel="stylesheet" href="assets/css/styles.css"  />
    </head>
    <body>

        <div id="main">

            <input type="dropbox-chooser" name="selected-file" id="db-chooser"
                        data-link-type="direct" class="hide" />
            <div id="content"></div>
            <button class="btn btn-inverse hide" type="button"
                        id="cropButton">Crop Image</button>

            <!-- Bootstrap Modal Dialogs -->

            <div id="cropModal" class="modal hide fade" role="dialog"
                        aria-hidden="true">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal"
                        aria-hidden="true">×</button>
                    <h4>Your cropped image</h4>
                </div>
                <div class="modal-body center"></div>
                <div class="modal-footer">
                    <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
                </div>
            </div>

            <div id="errorModal" class="modal hide fade" role="dialog" aria-hidden="true">
                <div class="modal-header">
                    <h4></h4>
                </div>
                <div class="modal-footer">
                    <button class="btn btn-danger" data-dismiss="modal"
                        aria-hidden="true">OK</button>
                </div>
            </div>

            <div id="progressModal" class="modal hide fade" role="dialog" aria-hidden="true">
                <div class="progress progress-striped active">
                    <div class="bar" style="width: 100%;"></div>
                </div>
            </div>

        </div>

        <!-- JavaScript Includes -->
        <script src="https://www.dropbox.com/static/api/1/dropbox.js"
            id="dropboxjs" data-app-key="z4ylr6z1qlivll4"></script>
        <script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
        <script src="assets/js/bootstrap.min.js"></script>
        <script src="assets/Jcrop/jquery.Jcrop.min.js"></script>
        <script src="assets/js/script.js"></script>

    </body>
</html>

En el cuerpo del documento, también tenemos el marcado que Twitter Bootstrap utilizará para mostrar los cuadros de diálogo modales. Tenemos tres modales con ID únicos:#cropModal , #errorModal y #progressModal . Se muestran con simples llamadas a funciones de jQuery (más sobre eso en un momento).

Familiarícese con el marcado, ya que es estándar para Bootstrap. Los modales pueden tener encabezados, cuerpos y pies de página opcionales, que tienen el estilo adecuado. Puedes poner cualquier código HTML que quieras en ellos. Al especificar clases, puede modificar el comportamiento de los cuadros de diálogo. Los atributos ocultos de aria están ahí para ocultar el contenido de los lectores de pantalla. Los botones tienen el data-dismiss="modal" atributo, que le dice a Bootstrap que debe vincular un detector de eventos en ellos y cerrar la ventana modal cuando se hace clic en ellos.

JQuery

El trabajo de jQuery es escuchar eventos en el botón de Dropbox, inicializar Jcrop con la imagen seleccionada y enviar una solicitud AJAX a crop.php . Aquí hay una descripción general de alto nivel de cómo sería el código:

activos/js/script.js

$(document).ready(function() {

    var cropButton      = $('#cropButton'),
        dbChooser       = $("#db-chooser"),
        errorModal      = $('#errorModal'),
        errorMessage    = errorModal.find('h4'),
        progressBar     = $('#progressModal'),
        cropModal       = $('#cropModal'),
        content         = $('#content');

    var coordinates, src,
        name, type,
        imgWidth, imgHeight,
        newWidth, newHeight,
        ratio, jcrop;

    dbChooser.on("DbxChooserSuccess", function(e) {

        // Here we will listen when a file is
        // chosen from dropbox, insert it into the page
        // and initialize the Jcrop plugin

    });

    function showCropButton(c) {
        // This function will called when we want to show
        // the crop button. This is executed when we have
        // made a selection with Jcrop.
    }

    function showError(err){
        // This function will display an error dialog
    }

    cropButton.click(function() {

        // This will send an AJAX requst to crop.php
        // with the dimensions of the crop area and
        // the URL of the image.

    });
});

El primer paso es crear una clave para su Selector de Dropbox. Debo decir que esta es la página de desarrollador más fácil y mejor pensada que he visto:la generación de claves de aplicación está integrada directamente allí y es fácil de usar. Solo presiona "Crear nueva aplicación " en la sección de configuración y complete sus detalles (el campo de dominios debe contener los dominios en los que utilizará el botón; para fines de desarrollo, es posible que desee incluir también localhost). Esto le dará una clave y un código para insertar que puede usar en su página; reemplácela con la mía en index.html.

Ahora que tenemos un botón en funcionamiento, tenemos que configurar una función de escucha de eventos para el evento de éxito. El objeto del evento contendrá la URL de la imagen de Dropbox, junto con atributos como el tamaño del archivo, el nombre y las miniaturas:

dbChooser.on("DbxChooserSuccess", function(e) {
    // Assigning the original event object, so we have access
    //to the files property passed by Dropbox:
    e = e.originalEvent;

    name = e.files[0].name;
    src = e.files[0].link;

    type = name.split('.');
    type = type[1] || '';

    if (type.toLowerCase() != 'jpg') {
        showError('This file type is not supported! Choose a jpg.');
        return false;
    }

    if (e.files[0].bytes > 1024*1024) {
        showError('Please choose an image smaller than 1MB!');
        return false;
    }

    // If we have previously initialized jCrop:

    if(jcrop){
        jcrop.destroy();
        cropButton.hide();
    }

    progressBar.modal('show');

    var img = $('<img>');

    img.load(function() {

        imgWidth = img.width();
        imgHeight = img.height();

        if (imgWidth >= 575 || imgHeight >= 575) {

            // The image is too large, resize it to fit a 575x575 square!

            if (imgWidth > imgHeight) {  // Wide

                ratio = imgWidth / 575;
                newWidth = 575;
                newHeight = imgHeight / ratio;

            } else {    // Tall or square

                ratio = imgHeight / 575;
                newHeight = 575;
                newWidth = imgWidth / ratio;

            }

        } else {

            ratio = 1;
            newHeight = imgHeight;
            newWidth = imgWidth;

        }

        // Remove the old styles
        img.removeAttr('style');

        // Set the new width and height
        img.width(newWidth).height(newHeight);

        // Initialize jCrop
        img.Jcrop({
            onChange : showCropButton,
            onSelect : showCropButton
        }, function(){
            // Save the jCrop instance locally
            jcrop = this;
        });

        // Hide the progress bar
        progressBar.modal('hide');
    });

    // Show the image off screen, so we can
    // calculate the width and height properly
    img.css({
        'position' : 'absolute',
        'top' : -100000,
        'left' : -100000,
        'visibility' : 'hidden',
        'display' : 'block'
    });

    // Set the SRC attribute and trigger the load
    // function when the image is downloaded

    content.html(img.attr('src', src));

});

Cuando el usuario realiza una selección con Jcrop, el showCropButton la devolución de llamada se llama con un objeto que contiene coordenadas, ancho y alto (consulte los documentos para obtener más ejemplos). Dentro de esa función, mostramos u ocultamos el #cropButton elemento dependiendo del tamaño del área seleccionada.

function showCropButton(c) {
    if (c.w == 0 || c.h == 0) {
        cropButton.hide();
    } else {
        cropButton.show();
        coordinates = c;
    }
}

Por último, solo tenemos que escribir las funciones para mostrar errores y enviar una solicitud AJAX.

function showError(err){
    errorMessage.text(err);
    errorModal.modal('show');
}

cropButton.click(function() {

    coordinates.x = Math.round(coordinates.x * ratio);
    coordinates.y = Math.round(coordinates.y * ratio);
    coordinates.w = Math.round(coordinates.w * ratio);
    coordinates.h = Math.round(coordinates.h * ratio);

    progressBar.modal('show');

    $.post('crop.php', {

        'coordinates' : coordinates,
        'src' : src

    }, function(r) {

        // Notice the "one" method - this
        // executes the callback only once

        progressBar.modal('hide').one('hidden', function() {

            cropModal.find('.modal-body').html('<img src="' + r + '" >');

            setTimeout(function() {
                cropModal.modal('show');
            }, 500);

        });

    });
});

¡Excelente! Ahora tenemos un ejemplo de trabajo. Todo lo que tenemos que hacer ahora es recortar la imagen. Para esto, escribiremos un breve script PHP.

El PHP

Este script recibirá una solicitud POST AJAX , con la URL de la imagen original de Dropbox y las coordenadas del área recortada. Luego usará las funciones de la Biblioteca GD para cambiar su tamaño y escribirlo en el disco. Antes de salir, hará eco del nombre temporal del cultivo, que jQuery mostrará.

recortar.php

$filename_length = 10;
$dir = 'tmp/'; // where to store the cropped images

if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['src'])) {

    $src = $_POST['src'];
    $coordinates = $_POST['coordinates'];

    $url = parse_url($src);
    $info = get_headers($src, 1);

    // Only allow photos from dropbox
    if ($url['host'] == 'dl.dropbox.com') {

        if ($info['Content-Type'] == 'image/jpeg' && $info['Content-Length'] < 1024*1024) {

            // Cache the remote file locally
            $cache = $dir . md5($src);

            if(!file_exists($cache)){
                file_put_contents($cache, file_get_contents($src));
            }

            // Original image
            $img = imagecreatefromjpeg($cache);

            // New image with the width and height of the crop
            $dst = imagecreatetruecolor($coordinates['w'], $coordinates['h']);

            // Copy and resize it depending on the crop area
            imagecopyresampled($dst, $img, 0, 0, $coordinates['x'], $coordinates['y'],
                $coordinates['w'], $coordinates['h'], $coordinates['w'], $coordinates['h']);

            // Generate a temporary name and write the file to disk
            $name = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyz".
                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, $filename_length);
            imagejpeg($dst, $dir . $name . '.jpg');

            // Print it for jQuery
            echo $dir . $name . '.jpg';

        } else {
            echo 1;
        }
    } else {
        echo 2;
    }

}

¡Con esto, nuestro ejemplo de carga y recorte de fotos de Dropbox está completo!

¡Listo!

Este tutorial es un ejemplo de muchas tecnologías trabajando juntas. Usamos Dropbox Chooser, Twitter Bootstrap, Jcrop, jQuery, AJAX y PHP con las funciones GD para crear esta demostración de recorte de imagen y espero que te sea útil.