从零实现Vue的组件库(四)- File-Reader实现

栏目: JavaScript · 发布时间: 7年前

内容简介:实现一个File-Reader组件用来读取本地资源。概述: 在用户手动上传一些资源的时候,需要分为两步,第一步是将其从本地读取出来,得到一个代码

实现一个File-Reader组件用来读取本地资源。

概述: 在用户手动上传一些资源的时候,需要分为两步,第一步是将其从本地读取出来,得到一个 file 对象,然后再上传至服务器。该组件用于第一步,然后可通过后续进一步封装程Upload组件。

该组件的痛点在于:
preventDefault

1. 实例

从零实现Vue的组件库(四)- File-Reader实现

代码

<!-- 基础用法 -->
<fat-filereader
    accept=".png, .jpg"
    :size="500 * 1024"
    @success="event => readHandler(event, 'file')"
    @error="errorHandler"
/>

<!-- 自定义上传区域 -->
<fat-filereader @success="event => readHandler(event, 'otherFile')">
    <div slot="clickarea" class="upload-area">
        <fat-icon class="icon" name="cloud_upload" size="24"/>
    </div>
</fat-filereader>

<!-- 拖拽上传 -->
<fat-filereader dragable @success="event => readHandler(event, 'anotherFile')">
    <div slot="clickarea" class="upload-area">
        <fat-icon class="icon" name="cloud_upload" size="24"/>
    </div>

    <span v-if="anotherFile.name">已上传:{{ anotherFile.name }}</span>
</fat-filereader>

<!-- 上传多个文件 -->
<fat-filereader multiple :limit="5" @success="multipleHandler" @error="errorHandler"/>
复制代码

实例地址:File-Reader 实例

代码地址: Github UI-Library

2. 原理

该组件的实现是基于原生的 <input type="file" /> ,再通过添加拖拽功能来以达到上图效果。

组件的基本结构如下,主要包含两个部分:

<slot name="clickarea"></slot>
< input type="file" />
<div
    :class="['file-reader', { 'is-disabled': disabled }]"
>
    <div 
        class="click-area" 
        @click.stop="eventHandler('readFile')"
        @dragenter="prevent"
        @dragover="prevent"
        @drop="event => dragable && eventHandler('dropReadFile', event)"
    >
        <slot name="clickarea">
            <fat-button :disabled="disabled" type="success">上传</fat-button>
        </slot>
    </div>
    <slot></slot>
    <input
        ref="input"
        type="file"
        class="is-hide"
        v-bind="$attrs"
        @change="event => eventHandler('change', event)"
        :disabled="disabled"
    >
  </div>
</template>
复制代码

<slot name="clickarea"></slot> 外层包裹 div 标签,用于监听该区域的点击事件。当它触发时,相关处理函数为

const handler = {
    readFile: () => {
        // fix change again
        this.$refs.input.value = "";
        this.$refs.input.click();
    },
    ...
};
复制代码

先重置 this.$refs.input.value ,不然会出现无法再上传的问题。然后,触发原生 <input type="file" /> 的点击事件,此时页面上会弹出原生的上传框。

监听该标签的 change 事件, @change="event => eventHandler('change', event) ,如果发生上传,会触发相关处理函数。

eventHandler(type, event = {}) {
    const handler = {
        ...
        // read files
        change: event => (this.sourceFiles = event.target.files)
    };
    handler[type] && handler[type](event);
}
复制代码

event 对象中,读取选中待上传的文件对象

从零实现Vue的组件库(四)- File-Reader实现

包含着 name 文件名, size 大小, type 类型等属性。

由于部分文件需要进行预览,例如img、svg等,所以需要生成对应的URL,基本方法为:

  • window.URL.createObjectURL :该方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL;
  • new FileReader :该对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,再通过 readAsDataURL 读取指定的 Blob 或 File 对象。

首先判断当前是否支持 window.URL && window.URL.createObjectURL ,如果支持,只需将已上传的 targetFiles 进行处理

Array.prototype.map.call(value, file => ({
    // file => url
    url: window.URL.createObjectURL(file),
    file
}));
复制代码

如果不支持该方法,则需要利用 File ReaderreadAsDataURL ,由于该方法是异步的,所以将其封装为Promise形式

const getReader = file => {
    return new Promise(function(resolve, reject) {
        const fileReader = new FileReader();
        // 读取相关文件
        fileReader.readAsDataURL(file);
        // fileReader onload 时,返回其结果
        fileReader.onload = () => resolve(fileReader.result);
        fileReader.onerror = () => fileReader.abort();
    });
};
const readers = Array.prototype.map.call(value, file => getReader(file));
复制代码

再利用 Promise.all 统一进行处理,结合已上传的 targetFiles 生成返回结果。

Promise.all(readers).then(results => {
    this.targetFiles = results.map((url, i) => ({
        url,
        file: value[i]
    }));
});
复制代码

以上完成基本的上传、预览功能。然后添加拖拽上传功能,在clickArea监听 dragenterdragoverdrop 事件。

<div 
    class="click-area" 
    @click.stop="eventHandler('readFile')"
    @dragenter="prevent"
    @dragover="prevent"
    @drop="event => dragable && eventHandler('dropReadFile', event)"
>
    <slot name="clickarea">
        <fat-button :disabled="disabled" type="success">上传</fat-button>
    </slot>
</div>
复制代码

相关处理函数为

eventHandler(type, event = {}) {
    const handler = {
        ...
        dropReadFile: event => {
            event.preventDefault();
            this.sourceFiles = event.dataTransfer.files;
        }
    };
    handler[type] && handler[type](event);
},
prevent(event) {
    event.preventDefault();
}
复制代码

利用 event.dataTransfer.files 获取相关上传文件,再通过 event.preventDefault 阻止游览器的一些默认行为,例如直接预览该文件等。获取到 sourceFiles 之后的操作等同于之前点击上传。

读取完成之后需要对文件进行校验,主要有上传文件的大小以及数量,相关代码如下

targetFiles(value) {
    const { size, limit } = this;
        if (value.length > limit) {
            this.$emit("error", {
                msg: "the quantity of files is too large its number cannot exceed"
            });
        } else {
            if (
                value.some(item => {
                    const {
                        file: { size: fileSize }
                    } = item;
                    return size && fileSize > size;
                })
            ) {
                this.$emit("error", {
                    msg: "file is too large its size cannot exceed"
                });
            } else {
                this.$emit("success", value);
            }
        }
    }
}

复制代码

3. 使用

相关的原生 <input type="file" />acceptmultiple 等属性,利用 v-bind=$attrs 传递给原生的 <input type="file" />

之后, 可以结合相关上传方法将其封装程Upload组件,以Axios为例

uploadFile (fileData) {
    const config = {
        // 依据当前环境配置
        baseURL: ...,
        headers: { 'Content-type': 'multipart/form-data' } 
    }
    let data = new FormData();
    data.append('file', fileData);
    data.append('name', fileData.name);
    
    return Axios.post('upload url', data, config)   
}
复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Haskell School of Expression

The Haskell School of Expression

Paul Hudak / Cambridge University Press / 2000-01 / USD 95.00

Functional programming is a style of programming that emphasizes the use of functions (in contrast to object-oriented programming, which emphasizes the use of objects). It has become popular in recen......一起来看看 《The Haskell School of Expression》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具