Arrastar e soltar HTML5 Cross Origin

Eu escrevi um plugin que está hospedado no GitHub que permite fazer uploads legais. Um dos recursos que possui é arrastar e soltar de origem cruzada, ou seja, arrastar uma imagem de um site para o uploader. Embora não funcione em alguns sites, por exemplo, página de resultados do Google Images ou anexos do Gmail (obviamente), funciona para a grande maioria dos sites. Hoje vou compartilhar com vocês os truques que fiz para fazê-lo funcionar.

Presumo que você entenda os conceitos básicos OOPe as funções de arrastar e soltar do HTML5

O JavaScript


var api = !!(win.Blob || win.File || win.FileList || win.FileReader),
canvtest
= document.createElement('canvas'),
canv
= !!(canvtest.getContext && canvtest.getContext('2d'));

document
.body.ondragover = dragOver;
document
.body.ondrop = dragDrop;

/**
* Determine whether a value is in an array

* @note Type sensitive

*
@param {Array} lookin The array to test
*
@param {Mixed} lookfor The thing to lookfor
* @returns {Boolean} True if 'lookfor' is in 'lookin'

*/

function in_array(lookin, lookfor) {
for (var i = 0; i < lookin.length; i++) {
if (lookin[i] === lookfor) {
return true;
}
}
return false;
}

/**
* Drag over event handler

*
@param {object(MouseEvent)} e
*/

function dragOver (e) {
var dt = e.dataTransfer;
if (!in_array(dt.types, 'Files') && in_array(dt.types, 'text/uri-list')) {
// The thing we are hovering with IS NOT a file, return.
return;
};
e
.stopPropagation();
e
.preventDefault();
dt
.dropEffect = 'copy';
}

/**
* Drop event handler

*
@param {object(MouseEvent)} e
*/

function dragDrop (e) {
// Prevent an accidental drop outside the drop zone
e
.stopPropagation();
e
.preventDefault();
if ($(e.target).closest('#mydropzonewrapper').length) {
// Only accept drops inside the drop zone
var dt = e.dataTransfer,
files
= dt.files;
if (!files.length && canv) {
// We may have a uri-list
var tdp = filesrc = dt.getData('url');
if (!filesrc) {
filesrc
= dt.getData('text/plain');
if (!filesrc) {
filesrc
= dt.getData('text/uri-list');
if (!filesrc) {
// We have tried all that we can to get this url but we can't. Abort mission
return;
}
}
}

$
.ajax({
url
: '/processing.php',
type
: 'post',
dataType
: 'json',
data
: {filesrc: filesrc, uploaddir: 'destinationfolder'}
}).done(function (e) {
if (e.result === 'OK') {
// The file has been successfully 'uploaded'
console
.log(e.data);
if (e.data.mimetype.match('image/*')) {
var img = new Image();
img
.onload = function () {
// Special image processing
};
img
.onerror = function () {
// Special image processing
};
// Add '?cachekill=unixtime' to prevent the browser from caching
img
.src = e.data.src + '?cachekill=' + (new Date().getTime());
} else {
// Normal processing
}
} else {
// There was an error
}
}).fail(function () {
// There was an error
});
return;
}
}
// Normal file processing
}

O PHP (processing.php)


cExternalUpload
::processExternalUpload();

class cExternalUpload {

static $settings;

/**
* Retrieve a file from another server, save it and pretend that it was an upload from our system

*/

static function processExternalUpload () {
$path
= filter_input(INPUT_POST, 'filesrc');
self::$settings = new stdClass();
self::$settings->uploaddir = filter_input(INPUT_POST, 'uploaddir');
self::setUploadDir();
try {
$pathbits
= explode('/', $path);
$filename
= substr(preg_replace('/[^a-z0-9_.]/', '_', strtolower(end($pathbits))), 0, 100);
$destination
= self::$settings->uploaddir . time() . '_' . $filename;
$relpath
= substr($destination, strlen(filter_input(INPUT_SERVER, 'DOCUMENT_ROOT')));
self::save($destination, $path);
$finfo
= finfo_open(FILEINFO_MIME_TYPE);
$mimetype
= finfo_file($finfo, $destination);
$output
= array(
'src' => $relpath,
'name' => $filename,
'mimetype' => $mimetype
);
if (preg_match("/image/.*/", $mimetype)) {
// The 'uploaded' file is an image
$size
= getimagesize($destination);
$output
['width'] = $output['croppedWidth'] = $output['resizedWidth'] = $size[0];
$output
['height'] = $output['croppedHeight'] = $output['resizedHeight'] = $size[1];
}
self::ajaxExit('OK', $output);
} catch (Exception $ex) {
self::ajaxExit('Fail', "Unable to get external file: {$ex->getMessage()}");
}
}

/**
* Send a JSON-encoded response from an Ajax call and exit

* @param $result string Message to return to the browser

* @param $data mixed Any additional data to return

* @param $status int Value of HTTP status to be sent to the browser

*/

private static function ajaxExit($result, $data = null, $status = 200) {
header
('Content-Type:text/json; charset=utf-8');
header
("HTTP/1.0 $status");
$response
= array('result' => $result);
if (!empty($data)) {
$response
['data'] = $data;
}
print json_encode($response);
exit;
}

/**
* Save an external file

* @param string $destination The destination of the upload

* @param string $src The path to the external file

* @return int The number of bytes written, or false on failure

* @throws Exception

*/

static function save($destination, $src) {
$ans
= @file_put_contents($destination, file_get_contents($src));
if ($ans === false) {
throw new Exception("Could not write data to {$destination}");
}
if (!(fileperms($destination) & 0020)) {
if (!chmod($destination, 0777)) {
throw new Exception('Failed to change permissions on ' . $destination);
}
}
return $ans;
}


/**
* Set the destination path for the uploaded file

* @return string The destination path

*/

private static function setUploadDir() {
$uploaddir
= self::$settings->uploaddir;
if(!preg_match("//$/", $uploaddir)) {
// The upload dir requires a '/' at the end
$uploaddir
= self::$settings->uploaddir = "{$uploaddir}/";
}
if(substr($uploaddir, 0, 1) === '/') {
$uploaddir
= self::$settings->uploaddir = substr($uploaddir, 0);
}
self::$settings->uploaddir = filter_input(INPUT_SERVER, 'DOCUMENT_ROOT') . "/$uploaddir";
}
}

Aproveite e comente se você não entender nada do código