文件点击上传和拖拽上传
2019-08-23 本文已影响2人
CondorHero
一、文件点击上传(事件代理)
核心思想就是模拟鼠标点击事件 <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]
}