php 文件拖拽上传,File杂谈——拖拽异步上传实现

在前一篇文章《File杂谈——拖拽上传前传》中我制作了一个静态的拖拽上传界面,拖拽文件到显示区域释放,可以显示拖入文件的基本信息。本文将在此基础上进一步加工,打造一个完整的拖拽上传示例。

示例说明

点击区域选择文件或直接将文件拖入区域,触发文件上传功能,文件将异步发送到服务器。待服务端处理完成后返回基本信息,在页面中显示。由于服务器容量问题,本示例未做文件保存处理,只是简单的将文件基本信息返回,文件上传的后端具体处理逻辑需要自行补充。

新的小伙伴FormData

我们知道,传统的文件上传如果要实现异步的效果,我们会使用iframe去模拟,或使用flash上传插件。但是今天,我们又认识了一位新成员——FromData,它可以通过js创建表单对象,并可以向该对象中添加表单数据(字符串、数字、文件等)。再结合我们熟悉的XMLHttpRequest对象,将表单数据异步提交到服务端,这样我们的问题就解决了。

下面,我们来看下核心代码:

function uploadFile(fs) {

var len = fs.length;

for (var i = 0; i < len; i++) {

sendFile(fs[i]);

}

}

function sendFile(file) {

var xhr = new XMLHttpRequest(),

fd = new FormData();

fd.append('file', file);

xhr.onreadystatechange = function() {

if (xhr.readyState == 4 && xhr.status == 200) {

// 将服务端返回信息输出到日志区(考虑多文件情况)

consoleDiv.innerHTML += '
' + xhr.responseText;

}

};

xhr.open('POST', './upload.php');

xhr.send(fd);

}

// 文件控件发生变化时,调用uploadFile函数,触发上传功能

file.onchange = function() {

uploadFile(this.files);

};

// 在区域内释放拖入文件时,调用文件上传函数

area.ondrop = function(ev) {

ev.preventDefault();

var dt = ev.dataTransfer;

uploadFile(dt.files);

};

代码很简单,不再做过多阐述。但是这里我想发表一点个人意见:根据示例我们不难发现有这么一个问题——如果用户都使用拖拽上传功能,而不使用点击触发File控件选择文件上传,那么File控件完全没有存在的必要。联系上文中我提到的File控件的地位受到威胁这一观点,我大胆的设想,假如未来的某一项标准中给每个HTMLElement暴露一个选择文件的功能接口,那么拖拽和点选功能将可以集于一个元素之上,到那时File控件的地位恐怕不仅仅是受到威胁,很有可能退出历史舞台!出于File控件视觉效果和交互不统一的角度去考虑,我觉得以上推测还是有可能的,哈哈~~

虽然示例并未在后端做太多工作,我这里还是以PHP为例,说明一下后端该如何工作。单从示例而言,我的代码是这样的:

$file = $_FILES['file'];

echo json_encode($file);

可以说是非常简单了。而我们在实际应用中往往还会涉及更多更复杂的处理逻辑。最起码的我们应该要将tmp_name对应的临时文件移动到我们指定的上传目录吧。当然,这一过程我们就会对文件类型进行判断,大小限制,重命名,数据保存,等等。基本代码:

$file = $_FILES['file'];

$path = './upload';

if ($file['size'] > 2000000) {

echo '{"error": "1000", "message": "上传文件大小超限,不能超过xxM"}';

}

$path .= '/file_' . time() . '.png';

// 这里还可能会存在文件数据保存,新旧名称关联等逻辑

move_uploaded_file($file['tmp_name'], $path);

一个神奇的方法sendAsBinary

前面我们聊到的使用FormData来实现文件异步上传,在高级浏览器中都能正常运作,没有太大问题。接下来我们另外一个在Firefox实现异步上传的方法。这个方法,我们又会交到一个新的朋友——FireReader。FileReader是HTML5新增的一个对象,它可以访问用户本地文件,并且可以以不同格式读取文件内容。

FileReader基本使用

首先我们来看一下如何创建一个FileReader实例对象,以及它拥有哪些实例方法。在js中创建一个FileReader对象很简单:

var reader = new FileReader();

我们可以通过reader对象访问本地文件,那么reader对象拥有哪些我们常用的属性、事件和方法呢?请看以下列表:

事件

onload:文件成功读完时触发

onloadend:文件读完时触发,无论成功与否

onloadstart:开始读取文件时触发

onprogress:文件读取中,常用于获取读取进度

onabort:文件读取操作中断

onerror:文件读取出错

属性

result:读取到的文件内容,当读取操作完成后生效

readyState:FileReader对象的当前状态

error:出错时的错误信息

方法

abort:中断文件读取操作

readAsBinaryString:将文件内容读取为二进制格式

readAsDataURL:将文件内容读取为DataURL格式,通常所说的base64格式

readAsText:将文件内容读取为文本

以上就是FileReader对象最常用的内容,下面我们先看一个小例子:

var rd = new FileReader();

rd.onload = function(ev) {

console.log(ev.target.result);

};

rd.readAsText(file);

以上代码中的file参数是一个file对象,可以使File控件的files属性中FileList的一个,也可以是dataTransfer中files属性中FileList的一个。

FileReader + sendAsBinary实现异步上传

认识了FireReader,下面我们来看一下在Firefox中如何使用FileReader和XMLHttpRequest的sendAsBinary方法实现文件异步上传。核心代码如下:

function sendByBinary(file) {

var xhr = new XMLHttpRequest(),

reader = new FileReader();

xhr.onreadystatechange = function() {

if (xhr.readyState == 4 && xhr.status == 200) {

consoleDiv.innerHTML += '
' + xhr.responseText;

}

};

xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');

xhr.open('POST', './upload.php');

reader.onload = function(ev) {

// 将二进制内容发送至服务端

xhr.sendAsBinary(ev.target.result);

};

// 将文件内容读取为二进制格式

reader.readAsBinaryString(file);

}

代码很简单,跟FormData的方式差不多,我们接着看服务端将如何获取POST过去的文件内容(以PHP为例):

// 方法一,这个方法需要php.ini开启支持

$content = $GLOBALS['HTTP_RAW_POST_DATA'];

// 方法二,不需要php.ini设置,内存压力小

$content = file_get_contents('php://input');

所以综合起来比较保险的方法:

$content = $GLOBALS['HTTP_RAW_POST_DATA'];

if (empty($content)) {

$content = file_get_contents('php://input');

}

echo $content; // 输出文件内容

我们暂且不说sendAsBinary这种方式目前只有Firefox支持,单从服务器拿到文件内容后该如何处理来说,这种方法显然没有使用FormData的方式有优势。因为服务端仅仅拿到了文件内容,并没有文件类型和大小等信息,对一些限制逻辑和文件存储的实现很不友好。

作者博客:百码山庄

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,uni-file-picker 是一款适用于 uni-app 的文件选择器组件,可以方便地进行文件上传操作,同时也支持拖拽上传的功能。 首先在页面中引入 uni-file-picker 组件: ```html <template> <view> <uni-file-picker @select="onSelect" @success="onSuccess" @fail="onFail" /> </view> </template> <script> import uniFilePicker from '@/components/uni-file-picker/uni-file-picker.vue' export default { components: { uniFilePicker }, methods: { onSelect(files) { console.log('选择了文件', files) }, onSuccess(res) { console.log('上传成功', res) }, onFail(err) { console.log('上传失败', err) } } } </script> ``` 在选择文件后,会触发 `select` 事件,返回一个文件列表,我们可以通过遍历列表来查看用户选择的文件。接着,当文件上传成功时,会触发 `success` 事件,返回上传的结果,当上传失败时,会触发 `fail` 事件,返回错误信息。 为了实现拖拽上传的功能,我们还需要在 `uni-file-picker` 组件中增加一些代码: ```html <template> <view class="uni-file-picker"> <view class="uni-file-picker__drop-area" @drop="onDrop" @dragover.prevent> <view class="uni-file-picker__icon"> <uni-icons type="add" size="32" color="#999"></uni-icons> </view> <view class="uni-file-picker__text">将文件拖到此区域上传</view> </view> <view class="uni-file-picker__button"> <button type="primary" @click="onChoose">选择文件</button> </view> <input class="uni-file-picker__input" ref="input" type="file" multiple @change="onChange" /> </view> </template> <script> export default { methods: { onDrop(e) { e.preventDefault() const files = e.dataTransfer.files this.$emit('select', files) }, onChoose() { this.$refs.input.click() }, onChange(e) { const files = e.target.files this.$emit('select', files) } } } </script> ``` 我们在 `uni-file-picker__drop-area` 上绑定了 `drop` 和 `dragover` 事件,分别用于监听文件拖拽和悬停操作,当用户将文件拖拽到该区域时,会触发 `drop` 事件,我们可以通过 `dataTransfer` 属性获取到用户拖拽文件列表,然后再通过 `$emit` 方法将文件列表传递出去。 需要注意的是,在拖拽操作中,浏览器会默认打开文件,因此需要在 `dragover` 事件中调用 `preventDefault` 方法阻止默认行为,避免浏览器打开文件。 最后,在 `uni-file-picker__button` 中添加一个按钮,用于触发文件选择操作,然后在 `uni-file-picker__input` 中绑定 `change` 事件,用于监听文件选择操作。 以上就是实现 uni-file-picker 文件上传并且实现拖拽的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值