From 1b1da9bfd740dbab1952e26fa3b25e40b5725345 Mon Sep 17 00:00:00 2001 From: joyqi Date: Mon, 14 Oct 2013 17:03:40 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=8B=96=E6=8B=BD=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/file-upload-js.php | 33 ++- admin/javascript/typecho.js | 555 ++++++++++++++++++++++++++++++++++++ var/Widget/Upload.php | 96 +++++-- 3 files changed, 652 insertions(+), 32 deletions(-) diff --git a/admin/file-upload-js.php b/admin/file-upload-js.php index daa6f2df..b965f753 100644 --- a/admin/file-upload-js.php +++ b/admin/file-upload-js.php @@ -29,7 +29,18 @@ $(document).ready(function() { }, onError : function (id, word) { $('#' + id).remove(); - alert(''); + alert(''); }, onComplete : function (id, url, data) { var li = $('#' + id).removeClass('loading').data('cid', data.cid) @@ -37,7 +48,7 @@ $(document).ready(function() { .data('image', data.isImage) .html('' + '' + data.title + ' ' + data.bits + + data.cid + '">' + data.title + ' ' + data.bytes + ' ' + ' ×') .effect('highlight', '#AACB36', 1000); @@ -47,6 +58,24 @@ $(document).ready(function() { }, }); + $('#upload-panel').filedrop({ + url : 'index('/action/upload' + . (isset($fileParentContent) ? '?cid=' . $fileParentContent->cid : '')); ?>', + allowedfileextensions : [allowedAttachmentTypes; + $attachmentTypesCount = count($attachmentTypes); + for ($i = 0; $i < $attachmentTypesCount; $i ++) { + echo '".' . $attachmentTypes[$i] . '"'; + if ($i < $attachmentTypesCount - 1) { + echo ','; + } + } +?>], + uploadStarted : function (i, file, len) { + console.log(file); + } + }); + $('#file-list li').each(function () { attachInsertEvent(this); attachDeleteEvent(this); diff --git a/admin/javascript/typecho.js b/admin/javascript/typecho.js index 29b994c1..4b0ad45c 100644 --- a/admin/javascript/typecho.js +++ b/admin/javascript/typecho.js @@ -546,3 +546,558 @@ jQuery.fn.extend( ); })($); +/*global jQuery:false, alert:false */ + +/* + * Default text - jQuery plugin for html5 dragging files from desktop to browser + * + * Author: Weixi Yen + * + * Email: [Firstname][Lastname]@gmail.com + * + * Copyright (c) 2010 Resopollution + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/mit-license.php + * + * Project home: + * http://www.github.com/weixiyen/jquery-filedrop + * + * Version: 0.1.0 + * + * Features: + * Allows sending of extra parameters with file. + * Works with Firefox 3.6+ + * Future-compliant with HTML5 spec (will work with Webkit browsers and IE9) + * Usage: + * See README at project homepage + * + */ +;(function($) { + + jQuery.event.props.push("dataTransfer"); + + var default_opts = { + fallback_id: '', + url: '', + refresh: 1000, + paramname: 'userfile', + requestType: 'POST', // just in case you want to use another HTTP verb + allowedfileextensions:[], + allowedfiletypes:[], + maxfiles: 25, // Ignored if queuefiles is set > 0 + maxfilesize: 1, // MB file size limit + queuefiles: 0, // Max files before queueing (for large volume uploads) + queuewait: 200, // Queue wait time if full + data: {}, + headers: {}, + drop: empty, + dragStart: empty, + dragEnter: empty, + dragOver: empty, + dragLeave: empty, + docEnter: empty, + docOver: empty, + docLeave: empty, + beforeEach: empty, + afterAll: empty, + rename: empty, + error: function(err, file, i, status) { + alert(err); + }, + uploadStarted: empty, + uploadFinished: empty, + progressUpdated: empty, + globalProgressUpdated: empty, + speedUpdated: empty + }, + errors = ["BrowserNotSupported", "TooManyFiles", "FileTooLarge", "FileTypeNotAllowed", "NotFound", "NotReadable", "AbortError", "ReadError", "FileExtensionNotAllowed"]; + + $.fn.filedrop = function(options) { + var opts = $.extend({}, default_opts, options), + global_progress = [], + doc_leave_timer, stop_loop = false, + files_count = 0, + files; + + $('#' + opts.fallback_id).css({ + display: 'none', + width: 0, + height: 0 + }); + + this.on('drop', drop).on('dragstart', opts.dragStart).on('dragenter', dragEnter).on('dragover', dragOver).on('dragleave', dragLeave); + $(document).on('drop', docDrop).on('dragenter', docEnter).on('dragover', docOver).on('dragleave', docLeave); + + this.on('click', function(e){ + $('#' + opts.fallback_id).trigger(e); + }); + + $('#' + opts.fallback_id).change(function(e) { + opts.drop(e); + files = e.target.files; + files_count = files.length; + upload(); + }); + + function drop(e) { + if( opts.drop.call(this, e) === false ) return false; + if(!e.dataTransfer) + return; + files = e.dataTransfer.files; + if (files === null || files === undefined || files.length === 0) { + opts.error(errors[0]); + return false; + } + files_count = files.length; + upload(); + e.preventDefault(); + return false; + } + + function getBuilder(filename, filedata, mime, boundary) { + var dashdash = '--', + crlf = '\r\n', + builder = '', + paramname = opts.paramname; + + if (opts.data) { + var params = $.param(opts.data).replace(/\+/g, '%20').split(/&/); + + $.each(params, function() { + var pair = this.split("=", 2), + name = decodeURIComponent(pair[0]), + val = decodeURIComponent(pair[1]); + + if (pair.length !== 2) { + return; + } + + builder += dashdash; + builder += boundary; + builder += crlf; + builder += 'Content-Disposition: form-data; name="' + name + '"'; + builder += crlf; + builder += crlf; + builder += val; + builder += crlf; + }); + } + + if (jQuery.isFunction(paramname)){ + paramname = paramname(filename); + } + + builder += dashdash; + builder += boundary; + builder += crlf; + builder += 'Content-Disposition: form-data; name="' + (paramname||"") + '"'; + builder += '; filename="' + filename + '"'; + builder += crlf; + + builder += 'Content-Type: ' + mime; + builder += crlf; + builder += crlf; + + builder += filedata; + builder += crlf; + + builder += dashdash; + builder += boundary; + builder += dashdash; + builder += crlf; + return builder; + } + + function progress(e) { + if (e.lengthComputable) { + var percentage = Math.round((e.loaded * 100) / e.total); + if (this.currentProgress !== percentage) { + + this.currentProgress = percentage; + opts.progressUpdated(this.index, this.file, this.currentProgress); + + global_progress[this.global_progress_index] = this.currentProgress; + globalProgress(); + + var elapsed = new Date().getTime(); + var diffTime = elapsed - this.currentStart; + if (diffTime >= opts.refresh) { + var diffData = e.loaded - this.startData; + var speed = diffData / diffTime; // KB per second + opts.speedUpdated(this.index, this.file, speed); + this.startData = e.loaded; + this.currentStart = elapsed; + } + } + } + } + + function globalProgress() { + if (global_progress.length === 0) { + return; + } + + var total = 0, index; + for (index in global_progress) { + if(global_progress.hasOwnProperty(index)) { + total = total + global_progress[index]; + } + } + + opts.globalProgressUpdated(Math.round(total / global_progress.length)); + } + + // Respond to an upload + function upload() { + stop_loop = false; + + if (!files) { + opts.error(errors[0]); + return false; + } + + if (opts.allowedfiletypes.push && opts.allowedfiletypes.length) { + for(var fileIndex = files.length;fileIndex--;) { + if(!files[fileIndex].type || $.inArray(files[fileIndex].type, opts.allowedfiletypes) < 0) { + opts.error(errors[3], files[fileIndex]); + return false; + } + } + } + + if (opts.allowedfileextensions.push && opts.allowedfileextensions.length) { + for(var fileIndex = files.length;fileIndex--;) { + var allowedextension = false; + for (i=0;i opts.maxfiles && opts.queuefiles === 0) { + opts.error(errors[1]); + return false; + } + + // Define queues to manage upload process + var workQueue = []; + var processingQueue = []; + var doneQueue = []; + + // Add everything to the workQueue + for (var i = 0; i < files_count; i++) { + workQueue.push(i); + } + + // Helper function to enable pause of processing to wait + // for in process queue to complete + var pause = function(timeout) { + setTimeout(process, timeout); + return; + }; + + // Process an upload, recursive + var process = function() { + + var fileIndex; + + if (stop_loop) { + return false; + } + + // Check to see if are in queue mode + if (opts.queuefiles > 0 && processingQueue.length >= opts.queuefiles) { + return pause(opts.queuewait); + } else { + // Take first thing off work queue + fileIndex = workQueue[0]; + workQueue.splice(0, 1); + + // Add to processing queue + processingQueue.push(fileIndex); + } + + try { + if (beforeEach(files[fileIndex]) !== false) { + if (fileIndex === files_count) { + return; + } + var reader = new FileReader(), + max_file_size = 1048576 * opts.maxfilesize; + + reader.index = fileIndex; + if (files[fileIndex].size > max_file_size) { + opts.error(errors[2], files[fileIndex], fileIndex); + // Remove from queue + processingQueue.forEach(function(value, key) { + if (value === fileIndex) { + processingQueue.splice(key, 1); + } + }); + filesRejected++; + return true; + } + + reader.onerror = function(e) { + switch(e.target.error.code) { + case e.target.error.NOT_FOUND_ERR: + opts.error(errors[4]); + return false; + case e.target.error.NOT_READABLE_ERR: + opts.error(errors[5]); + return false; + case e.target.error.ABORT_ERR: + opts.error(errors[6]); + return false; + default: + opts.error(errors[7]); + return false; + }; + }; + + reader.onloadend = !opts.beforeSend ? send : function (e) { + opts.beforeSend(files[fileIndex], fileIndex, function () { send(e); }); + }; + + reader.readAsDataURL(files[fileIndex]); + + } else { + filesRejected++; + } + } catch (err) { + // Remove from queue + processingQueue.forEach(function(value, key) { + if (value === fileIndex) { + processingQueue.splice(key, 1); + } + }); + opts.error(errors[0]); + return false; + } + + // If we still have work to do, + if (workQueue.length > 0) { + process(); + } + }; + + var send = function(e) { + + var fileIndex = (e.srcElement || e.target).index; + + // Sometimes the index is not attached to the + // event object. Find it by size. Hack for sure. + if (e.target.index === undefined) { + e.target.index = getIndexBySize(e.total); + } + + var xhr = new XMLHttpRequest(), + upload = xhr.upload, + file = files[e.target.index], + index = e.target.index, + start_time = new Date().getTime(), + boundary = '------multipartformboundary' + (new Date()).getTime(), + global_progress_index = global_progress.length, + builder, + newName = rename(file.name), + mime = file.type; + + if (opts.withCredentials) { + xhr.withCredentials = opts.withCredentials; + } + + var data = atob(e.target.result.split(',')[1]); + if (typeof newName === "string") { + builder = getBuilder(newName, data, mime, boundary); + } else { + builder = getBuilder(file.name, data, mime, boundary); + } + + upload.index = index; + upload.file = file; + upload.downloadStartTime = start_time; + upload.currentStart = start_time; + upload.currentProgress = 0; + upload.global_progress_index = global_progress_index; + upload.startData = 0; + upload.addEventListener("progress", progress, false); + + // Allow url to be a method + if (jQuery.isFunction(opts.url)) { + xhr.open(opts.requestType, opts.url(), true); + } else { + xhr.open(opts.requestType, opts.url, true); + } + + xhr.setRequestHeader('content-type', 'multipart/form-data; boundary=' + boundary); + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + + // Add headers + $.each(opts.headers, function(k, v) { + xhr.setRequestHeader(k, v); + }); + + xhr.sendAsBinary(builder); + + global_progress[global_progress_index] = 0; + globalProgress(); + + opts.uploadStarted(index, file, files_count); + + xhr.onload = function() { + var serverResponse = null; + + if (xhr.responseText) { + try { + serverResponse = jQuery.parseJSON(xhr.responseText); + } + catch (e) { + serverResponse = xhr.responseText; + } + } + + var now = new Date().getTime(), + timeDiff = now - start_time, + result = opts.uploadFinished(index, file, serverResponse, timeDiff, xhr); + filesDone++; + + // Remove from processing queue + processingQueue.forEach(function(value, key) { + if (value === fileIndex) { + processingQueue.splice(key, 1); + } + }); + + // Add to donequeue + doneQueue.push(fileIndex); + + // Make sure the global progress is updated + global_progress[global_progress_index] = 100; + globalProgress(); + + if (filesDone === (files_count - filesRejected)) { + afterAll(); + } + if (result === false) { + stop_loop = true; + } + + + // Pass any errors to the error option + if (xhr.status < 200 || xhr.status > 299) { + opts.error(xhr.statusText, file, fileIndex, xhr.status); + } + }; + }; + + // Initiate the processing loop + process(); + } + + function getIndexBySize(size) { + for (var i = 0; i < files_count; i++) { + if (files[i].size === size) { + return i; + } + } + + return undefined; + } + + function rename(name) { + return opts.rename(name); + } + + function beforeEach(file) { + return opts.beforeEach(file); + } + + function afterAll() { + return opts.afterAll(); + } + + function dragEnter(e) { + clearTimeout(doc_leave_timer); + e.preventDefault(); + opts.dragEnter.call(this, e); + } + + function dragOver(e) { + clearTimeout(doc_leave_timer); + e.preventDefault(); + opts.docOver.call(this, e); + opts.dragOver.call(this, e); + } + + function dragLeave(e) { + clearTimeout(doc_leave_timer); + opts.dragLeave.call(this, e); + e.stopPropagation(); + } + + function docDrop(e) { + e.preventDefault(); + opts.docLeave.call(this, e); + return false; + } + + function docEnter(e) { + clearTimeout(doc_leave_timer); + e.preventDefault(); + opts.docEnter.call(this, e); + return false; + } + + function docOver(e) { + clearTimeout(doc_leave_timer); + e.preventDefault(); + opts.docOver.call(this, e); + return false; + } + + function docLeave(e) { + doc_leave_timer = setTimeout((function(_this) { + return function() { + opts.docLeave.call(_this, e); + }; + })(this), 200); + } + + return this; + }; + + function empty() {} + + try { + if (XMLHttpRequest.prototype.sendAsBinary) { + return; + } + XMLHttpRequest.prototype.sendAsBinary = function(datastr) { + function byteValue(x) { + return x.charCodeAt(0) & 0xff; + } + var ords = Array.prototype.map.call(datastr, byteValue); + var ui8a = new Uint8Array(ords); + + // Not pretty: Chrome 22 deprecated sending ArrayBuffer, moving instead + // to sending ArrayBufferView. Sadly, no proper way to detect this + // functionality has been discovered. Happily, Chrome 22 also introduced + // the base ArrayBufferView class, not present in Chrome 21. + if ('ArrayBufferView' in window) + this.send(ui8a); + else + this.send(ui8a.buffer); + }; + } catch (e) {} + +})(jQuery); diff --git a/var/Widget/Upload.php b/var/Widget/Upload.php index db6d2cf5..4bb6d205 100644 --- a/var/Widget/Upload.php +++ b/var/Widget/Upload.php @@ -108,10 +108,10 @@ class Widget_Upload extends Widget_Abstract_Contents implements Widget_Interface if (!move_uploaded_file($file['tmp_name'], $path)) { return false; } - } else if (isset($file['bits'])) { + } else if (isset($file['bytes'])) { //直接写入文件 - if (!file_put_contents($path, $file['bits'])) { + if (!file_put_contents($path, $file['bytes'])) { return false; } } else { @@ -173,10 +173,10 @@ class Widget_Upload extends Widget_Abstract_Contents implements Widget_Interface if (!move_uploaded_file($file['tmp_name'], $path)) { return false; } - } else if (isset($file['bits'])) { + } else if (isset($file['bytes'])) { //直接写入文件 - if (!file_put_contents($path, $file['bits'])) { + if (!file_put_contents($path, $file['bytes'])) { return false; } } else { @@ -307,23 +307,41 @@ class Widget_Upload extends Widget_Abstract_Contents implements Widget_Interface /** 增加插件接口 */ $this->pluginHandle()->upload($this); - echo "'; - return; + if ($this->request->isAjax()) { + $this->response->throwJson(array($this->request->_id, $this->attachment->url, array( + 'cid' => $insertId, + 'title' => $this->attachment->name, + 'type' => $this->attachment->type, + 'size' => $this->attachment->size, + 'bytes' => number_format(ceil($this->attachment->size / 1024)) . ' Kb', + 'isImage' => $this->attachment->isImage, + 'url' => $this->attachment->url, + 'permalink' => $this->permalink + ))); + } else { + echo "'; + + return; + } } } } - echo ""; + if ($this->request->isAjax()) { + $this->response->throwJson(false); + } else { + echo ""; + } } /** @@ -367,23 +385,41 @@ class Widget_Upload extends Widget_Abstract_Contents implements Widget_Interface /** 增加插件接口 */ $this->pluginHandle()->modify($this); - echo "'; - return; + if ($this->request->isAjax()) { + $this->response->throwJson(array($this->request->_id, $this->attachment->url, array( + 'cid' => $this->cid, + 'title' => $this->attachment->name, + 'type' => $this->attachment->type, + 'size' => $this->attachment->size, + 'bytes' => number_format(ceil($this->attachment->size / 1024)) . ' Kb', + 'isImage' => $this->attachment->isImage, + 'url' => $this->attachment->url, + 'permalink' => $this->permalink + ))); + } else { + echo "'; + + return; + } } } } - echo ""; + if ($this->request->isAjax()) { + $this->response->throwJson(false); + } else { + echo ""; + } } /**