文件点击上传和拖拽上传

作者: CondorHero | 来源:发表于2019-08-23 21:34 被阅读2次
    一、文件点击上传(事件代理)

    核心思想就是模拟鼠标点击事件 <input type="file" />还是文件上传的渠道,只不过我们需要把它给隐藏掉,加个 hidden 属性 <input type="file" hidden/> 虽然标签仍然在,但是我们看不见了,现在我们只需要,点击页面的加号图标点击图标来唤醒文件上传框。
    核心代码:

    onClick = {()=>{
        let evt = document.createEvent("mouseEvents");
        evt.initMouseEvent("click",false,false);
        this.refs.file.dispatchEvent(evt);
    }}
    
    模拟表单提交

    源代码展示:

    import React, { Component } from 'react';
    import { Progress , Icon , Button} from 'antd';
    import classnames from "classnames";
    import axios from "axios";
    export default class S1 extends Component {
        constructor(){
            super();
            this.state = {
                base64 : "",
                progress : 0
            }
        }
        render() {
            return (
                <div>   
                        <div className = "img" style = {{
                            width: "400px",
                            height: "300px",
                            textAlign:"center",
                            border: "1px dashed #457791",
                            backgroundImage:`url(${this.state.base64})`
                        }}>
                            <div>
                                <Icon type="plus-circle" style={{ fontSize: '150px', color: '#08c' , marginTop:'20px'}} onClick = {()=>{
                                    let evt = document.createEvent("mouseEvents");
                                    evt.initMouseEvent("click",false,false);
                                    this.refs.file.dispatchEvent(evt);
                                }}/>
                            </div>
                                <Button style={{ marginTop:'40px'}} onClick = {()=>{
                                // 点击提交创建虚拟表单
                                let vform = new FormData();
                                // 追加文件
                                vform.append('file',this.refs.file.files[0]);
                                axios({
                                    method: 'post',
                                    url: '/api/uppic',
                                    data: vform,
                                    onUploadProgress: (progressEvent)=> {
                                        // ~~()取整
                                        this.setState({
                                            progress : ~~(progressEvent.loaded / progressEvent.total * 100)
                                        })
                                    }
                                }).then(data=>{
                                    this.setState({
                                        filename : data.data.filename
                                    })
                                });
                            }}>提交</Button>
                            {/*上传进度条*/}
                            <div className = "img2">
                                <Progress className = {classnames({
                                        isHidden:this.state.progress <= 0 || this.state.progress >= 100
                                    })} type="line" strokeColor = "primary" percent={this.state.progress} width = {100} />
                            </div>
                            <input type = "file" ref = "file" hidden multiple onChange = {(e)=>{
                                let file = e.target.files[0];
                                // HTML的新特性可以把图片变成base64地址
                                let fr = new FileReader();
                                fr.readAsDataURL(file);
                                fr.onload = (_e)=>{
                                    this.setState({
                                        base64 : _e.target.result
                                    })
                                }
                            }}/>
                        </div>
                </div>
            )
        }
    }
    
    五、文件拖拽上传

    拖拽上传的思路:

    • 阻止浏览器的默认事件,拖拽图片到浏览器页面当我们松手的时候,浏览器会默认跳转打开这个图片,第一步就是阻止这步的发生,这个过程涉及到了两个事件,我们只需要阻止这两个事件就行了:
    // 移动拖着不放事件
    document.addEventListener("dragover",function(e){
        console.log("拖着不放事件!");
        e.preventDefault();
    });
    // 移动拖着放下事件
    document.addEventListener("drop",function(e){
        console.log("拖着放下事件!");
        e.preventDefault();
    });
    

    阻止这两个事件,就发现拖拽图片到浏览器(其实任何文件都行)已经没有任何效果了。


    阻止浏览器默认事件
    • 能召唤出文件夹,这步虚拟事件
    let evt = document.createEvent("mouseEvents");
    evt.initMouseEvent("click",false,false);
    this.refs.file.dispatchEvent(evt);
    
    • 拿到放到浏览器上图片的地址
      这步使用一个事件完成。onDrop 事件, e.dataTransfer.files 是一个对象,里面包含所有拖拽上来的图片信息。

    比如下面我拖拽上来的八张图片信息打印如下:


    最终的实验效果:


    图片拖拽效果

    完整代码及注释:

    import React, { Component } from 'react';
    
    import {Icon} from "antd";
    
    export default class S2 extends Component {
        constructor(){
            super();
            this.state = {
                arr : [],
                base64:[]
            }
        }
        componentDidMount(){
            // 移动拖着不放事件
            document.addEventListener("dragover",function(e){
                console.log("拖着不放事件!");
                e.preventDefault();
            });
            // 移动拖着放下事件
            document.addEventListener("drop",function(e){
                console.log("拖着放下事件!");
                e.preventDefault();
            });
        }
        render() {
            return (
                <div className = "box" onDrop = {(e)=>{
                    // 这是个对象e.dataTransfer.files
                    console.log(e.dataTransfer.files.length)
                    // 拿到所有图片的地址,把他们放到数组里面好进行下步操作
                    this.setState({
                        arr : [...e.dataTransfer.files]
                    },()=>{
                        console.log(this.state.arr);
                        // 这步把所有的图片全部变成base64地址格式
                        // 为图片预览提供准备
                        this.state.arr.map(item => {
                            let file = item;
                            // HTML的新特性可以把图片变成base64地址
                            let fr = new FileReader();
                            fr.readAsDataURL(file);
                            fr.onload = (_e)=>{
                                this.setState({
                                    base64 : [...this.state.base64,_e.target.result]
                                })
                            }
                        })
                    })
                }}>
                {/*这是个加号*/}
                <Icon  onClick = {()=>{
                    // 虚拟表单事件,用来打开文件夹
                    let evt = document.createEvent("mouseEvents");
                    evt.initMouseEvent("click",false,false);
                    this.refs.file.dispatchEvent(evt);
                }} type="plus-circle" style={{ fontSize: '150px',position:"absolute",left:"50%",top:"50%",transform:"translate(-50%,-50%)"}} />
                {/*这是需要被代理的事件*/}
                <input type = "file" ref = "file" hidden multiple />
                {/*base64位的提供的地址进行预览*/}
                {
                    this.state.base64.map((item,index) => <img  className = "cur" key = {index} src = {item}/>)
                }
                </div>
            )
        }
    }
    

    还有对应的 css 样式:

    .box{
        width: 500px;
        height: 300px;
        background-color: #f6f6f6;
        border:1px dashed #343;
        position: relative;
    }
    .cur{
        width: 100px;
        margin:10px;
    }
    
    三、修复一个 bug

    这是第二次修改:

    上面写的拖拽效果是非常的好。这个 bug 是不是很直观,哈哈哈哈。



    解决办法也很简单,去除图片的默认点击事件:

    {
        this.state.base64.map((item,index) => <img onMouseDown = {(e)=>{
            e.preventDefault()
        }} className = "cur" key = {index} src = {item}/>)
    }
    

    如此,现在放在页面上的图片已经没了鼠标按下的事件,现在任你怎么拖都没效果了。

    这是第三次修改:

    再次修正错误我发现不是因为图片没去掉默认事件的问题。而是因为代码段多了个...this.state.arr 所以才会导致上面那个 bug 。数组应该时刻保持是新的。

     this.setState({
                        arr : [...this.state.arr,...e.dataTransfer.files]
                    }
    

    相关文章

      网友评论

        本文标题:文件点击上传和拖拽上传

        本文链接:https://www.haomeiwen.com/subject/zadxectx.html