我正在构建一个基于 XHR 的 JavaScript 文件上传器。(实际上是 Ajax,因为我使用 jQuery)我使用一些 HTML5 功能作为 Drag&Drop API,以及 - 目前 - FileReader API。
我对这个过程有一些疑问,因为理论上有些东西我不明白。
由于某些原因,我自愿选择不使用现有且设计良好的插件/独立上传器之一:
- 我有一些特定的需求,例如为项目添加标题和描述(如在 facebook 中)
- 我会自己实现它作为 HTML5 API 的尝试。
我已经阅读了使用 Ajax 上传文件的教程,但我发现使用 FileReader API 最终将图像附加到 FormData API 很奇怪。
更重要的是,我真的不想有一个表单,我只是将图像放在一个放置区域,使用 FileReader API 获取文件内容,然后通过 AJAX 请求将其发送到我的服务器。
我只是想知道是否有人有更好的流程想法,因为目前:
- 我得到删除的文件并读取它的内容(异步,但需要一些时间......)
- 我将此内容与 AJAX 请求一起发送到服务器
- 我将内容写入服务器上的物理文件(再花一些时间)
我不知道如何优化/更快,但是看到大图的时间是愚蠢的,我只是不想通过示例来想象视频......是否可以通过示例仅使用 Ajax 发送文件的引用,并直接管理上传,而不是获取文件内容然后等待用户单击发送按钮发送它?
例如,facebook 上传器是如何工作的,他们似乎解决了问题/使用了一些东西来解决它?
(如果有人有兴趣得到它或看看,这里是我目前所做的一个小样本)
/* HTML */
<div id='drop-area'>
<h3 class='placeholder'>Drop your files there!</h3>
</div>
/* CSS */
#drop-area {
position: relative;
width: 100%;
height: 400px;
border: 1px dashed grey;
border-radius: 10px;
}
#drop-area.drag-over {
border: 1px solid cyan;
}
#drop-area .placeholder {
position: absolute;
top: 50%;
left: 50%;
width: 235px;
height: 25px;
margin: -12px 0 0 -117px;
line-height: 25px;
text-transform: uppercase;
}
/* JavaScript - jQuery way */
// This code is NOT AT ALL full-browser compatible !!
$(document).ready(function () {
var i,
$dropArea = $('drop-area'),
fileReader,
file,
result,
sendRequest = function () {
$.ajax({
url: 'upload.php',
type: 'post',
data: {
title: $('#title').val(),
description: $('#description').val(),
data: result, // base64 encoded data
type: file.type
},
processData: false, // No query string
contentType: false // No data formatting
}).fail(function (error, status) {
console.log(error, status);
alert('Fail...');
}).done(function (response, output) {
console.log(response, output);
alert('Uploaded!');
}).always(function () {
$('#file-form-container').empty().detach();
});
},
onFileFormSubmit = function (e) {
e.preventDefault();
sendRequest();
return false;
},
displayForm = function (data) {
$('body').append($('<div/>', {
'id': 'file-form-container'
}).append($('<form/>', {
'action': '#',
'id': 'file-form'
}).append($('<img/>', {
'src': data,
'alt': 'File',
'width': 100px
})).append($('<input/>', {
'id': 'title',
'type': 'text',
'name': 'title'
})).append($('<textarea/>', {
'id': 'description',
'class': 'description',
'name': 'description'
})).append($('<input/>', {
'type': 'submit',
'name': 'submit',
'value': 'Send!'
}))));
},
onProgress = function (e) {
console.log('progress', e);
// Update a <progress> element...
},
onLoadEnd = function (e) {
console.log('load end', e);
result = e.target.result;
displayForm(result);
},
onError = function (e) {
console.log('error', e);
// Display a message...
}
readFile = function (file) {
fileReader = new FileReader();
fileReader.onprogress = onProgress;
fileReader.onerror = onError;
fileReader.onloadend = onLoadEnd;
fileReader.readAsDataURL(file);
};
$dropArea.on('dragenter', function (e) {
$(this).addClass('drag-over');
}).on('dragover', function (e) {
e.preventDefault();
e.originalEvent.dataTransfer.dropEffect = 'copy'; // Check the browser support if you want to fix the compatibility
}).on('dragleave', function (e) {
e.preventDefault();
$(this).removeClass('drag-over');
}).on('dragend', function (e) {
e.preventDefault();
$(this).removeClass('drag-over');
}).on('drop', function (e) {
// e || e.originalEvent ?
e.preventDefault();
e.stopPropagation();
$(this).removeClass('drag-over');
if (e.originalEvent.dataTransfer.files) {
for (i = 0, i < e.originalEvent.dataTransfer.files.length; i++) {
// Work with arrays to manage multiple files
file = e.originalEvent.dataTransfer.files[i];
readFile(file); // Read the file content then process the upload (callbacks)
}
}
})
});
/* PHP - Symfony2 */
<?php
// Symfony 2 Controller Context
$em = $this->getDoctrine()->getEntityManager();
// get params
$request = $this->getRequest();
// Check for types, sizes, errors etc.
if ($request->get('data') && [...]) {
[...]
}
// write the decoded data into a file
// fwrite(base64_decode(...));
// By the way, is there any OOP - MVC way to do that with Symfony2 ?
$path = ''; // define the file path
// Save the entity
$entity = new Picture();
$entity->setTitle($request->get('title'));
$entity->setDescription($request->get('description'));
$entity->setPath($path);
$em->persist($entity);
$em->flush();
return new JsonResponse(array($entity), 202);
?>