React之Upload上传组件

React之Upload上传组件

生成项目

create-react-app hsdrag --typescript
cd hsdrag
cnpm install antd koa  koa-body koa-static -S
cnpm start

上传组件

src\index.tsx

src\index.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
//import Dragger from './Dragger';
import { Upload, Icon, message } from 'antd';
const { Dragger } = Upload;
const props = {
    name: 'file',
    action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
    onChange(info: any) {
        console.log(info);
        const { status } = info.file;
        if (status === 'done') {
            message.success(`${info.file.name} 上传成功!`);
        } else if (status === 'error') {
            message.error(`${info.file.name} 上传失败!`);
        }
    },
};
ReactDOM.render(
    <Dragger {...props}><Icon type="inbox" /></Dragger>,
    document.getElementById('root'),
);

Dragger\index.tsx

src\Dragger\index.tsx

import React from 'react';
interface Props {
}
const Dragger: React.SFC<Props> = function (props: Props): JSX.Element {
    return (
        <div>
            Dragger
        </div>
    )
}
export default Dragger;

布局上传组件

src\Dragger\index.tsx

Dragger\index.tsx

import React from 'react';
import './index.css';
type Props = React.PropsWithChildren<{

}>
const Dragger: React.SFC<Props> = function (props: Props): JSX.Element {
    return (
        <div className="dragger-container">
            {props.children}
        </div>
    )
}
export default Dragger;

src\Dragger\index.css

src\Dragger\index.css

.dragger-container{
    position: relative;
    width: 100%;
    height:200px;
    background: #FAFAFA;
    border:1px dashed #D9D9D9;
    border-radius: 4px;
    cursor: pointer;
    display: flex;
    justify-content: center;
    align-items: center;
    transition: border-color .8s;
}
.dragger-container:hover{
    border-color:#40A9FF;
}
.dragger-container i{
    font-size:100px;
}

进度条

src\index.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import Dragger, { UploadFile, DragProps } from './Dragger';
import { Upload, Icon, message } from 'antd';
//const { Dragger } = Upload;
const props: DragProps = {
    name: 'file',
    //action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
    action: 'http://localhost:8080/upload',
    onUpload(uploadFile: UploadFile) {
        console.log(uploadFile);
        if (uploadFile.error) {
            message.error(`${uploadFile.file.name} 上传失败!`);
        } else {
            message.success(`${uploadFile.file.name} 上传成功!`);
        }
    },
};
ReactDOM.render(
    <Dragger {...props}><Icon type="inbox" /></Dragger>,
    document.getElementById('root'),
);

Dragger\index.tsx

  • useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)返回的 ref 对象在组件的整个生命周期内保持不变
  • useEffect 就是一个 Effect Hook,给函数组件增加了操作副作用的能力。它跟 class 组件中的 componentDidMount、componentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个 API
  • useState 就是一个 Hook,通过在函数组件里调用它来给组件添加一些内部 state,React 会在重复渲染时保留这个 state,useState 会返回一对值:当前状态和一个让你更新它的函数

拖动目标上触发事件(源元素)

事件触发
ondragstart用户开始拖动元素时触发
ondrag元素正在拖动时触发
ondragend用户完成元素拖动后触发

释放目标时触发的事件

事件触发
ondragenter当被鼠标拖动的对象进入其容器范围内时触发此事件
ondragover当某被拖动的对象在另一对象容器范围内拖动时触发此事件,如果需要设置允许放置,我们必须阻止对元素的默认处理方式
ondragleave当被鼠标拖动的对象离开其容器范围内时触发此事件
ondrop在一个拖动过程中,释放鼠标键时触发此事件

src\Dragger\index.tsx

import React, {
    useRef, MutableRefObject, RefObject, useEffect, useState
} from 'react';
import './index.css';
import { Progress, Icon } from 'antd';
export type DragProps = React.PropsWithChildren<{
    onUpload: any;
    name: string;
    action: string
}>
export interface UploadFile {
    file: File;
    percent?: number;
    url?: string;
    uploading?: boolean;
    error?: boolean
}
const Dragger: React.SFC<DragProps> = function (props: DragProps): JSX.Element {
    let [uploadFiles, setUploadFiles] = useState<Array<UploadFile>>([]);
    let uploadContainer: MutableRefObject<HTMLDivElement | undefined> = useRef<HTMLDivElement>();
    const onDragEnter: (ev: DragEvent) => any = (ev: DragEvent): any => {
        ev.preventDefault();
        ev.stopPropagation();
    };
    const onDragOver = (ev: DragEvent): any => {
        ev.preventDefault();
        ev.stopPropagation();
    };
    const onDragLeave = (ev: DragEvent): any => {
        ev.preventDefault();
        ev.stopPropagation();
    };
    const onDrop = (ev: DragEvent): any => {
        ev.preventDefault();
        ev.stopPropagation();
        let transfer: DataTransfer | null = ev.dataTransfer;
        if (transfer && transfer.files) {
            upload(transfer.files);
        }
    };
    function upload(files: DataTransfer['files']) {
        for (let i = 0; i < files.length; i++) {
            let file = files[i];
            let formData = new FormData();
            formData.append('filename', file.name);
            formData.append(props.name, file);
            var xhr: XMLHttpRequest = new XMLHttpRequest();
            xhr.open('POST', props.action, true);
            xhr.responseType = 'json';
            let uploadFile: UploadFile = { file, percent: 0, uploading: true, error: false };
            xhr.onreadystatechange = function () {
                if (xhr.readyState == 4 && xhr.status === 200) {
                    debugger;
                    uploadFile.url = xhr.response.url;
                    props.onUpload(uploadFile);
                }
            }
            uploadFiles.push(uploadFile);
            xhr.onprogress = updateProgress;
            xhr.upload.onprogress = updateProgress;
            function updateProgress(event: ProgressEvent) {
                if (event.lengthComputable) {
                    // 上传的过程中,会不停地触发onprogress事件
                    let percent: number = parseInt((event.loaded / event.total * 100).toFixed(0));
                    uploadFile.percent = percent;
                    if (percent >= 100) {
                        uploadFile.uploading = false;
                    }
                    setUploadFiles([...uploadFiles]);
                }
            }
            xhr.onerror = function () {
                uploadFile.error = true;
                uploadFile.uploading = false;
                setUploadFiles([...uploadFiles]); // 要解构才能
            }
            xhr.ontimeout = function () {
                uploadFile.error = true;
                uploadFile.uploading = false;
                setUploadFiles([...uploadFiles]);
            }
            xhr.send(formData);
        }
    }
    useEffect(() => {
        uploadContainer.current!.addEventListener('dragenter', onDragEnter);
        uploadContainer.current!.addEventListener('dragover', onDragOver);
        uploadContainer.current!.addEventListener('drop', onDrop);
        uploadContainer.current!.addEventListener('dragleave', onDragLeave);
        return () => {
            uploadContainer.current!.removeEventListener('dragenter', onDragEnter);
            uploadContainer.current!.removeEventListener('dragover', onDragOver);
            uploadContainer.current!.removeEventListener('drop', onDrop);
            uploadContainer.current!.removeEventListener('dragleave', onDragLeave);
        }
    })
    return (
        <>
            <div className="dragger-container" ref={uploadContainer as RefObject<HTMLDivElement> | null | undefined}>
                {props.children}
            </div>
            {
                uploadFiles.map((uploadFile: UploadFile, index: number) => (
                    <div key={index}>
                        <div>
                            {!uploadFile.error && <Icon type={uploadFile.uploading ? 'loading' : 'paper-clip'} />}
                            <span style={{ marginLeft: 10 }}>{uploadFile.file.name}</span>
                        </div>
                        <Progress status={uploadFile.error ? 'exception' : undefined} key={index} percent={uploadFile.percent} />
                    </div>
                ))
            }
        </>
    )
}
export default Dragger;

后端接口

let koaStatic = require('koa-static');
let path = require('path');
let koaBody = require('koa-body');
let fs = require('fs');
let Koa = require('koa');
let app = new Koa();
app.use(async (ctx, next) => {
    ctx.set('Access-Control-Allow-Origin', '*');
    ctx.set('Access-Control-Allow-Headers', 'Content-Type, Accept');
    ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
    if (ctx.method == 'OPTIONS') {
        ctx.body = 200;
    } else {
        await next();
    }
});
app.use(koaBody({
    formidable: { uploadDir: path.resolve(__dirname, './uploads') },
    multipart: true
}));

app.use(koaStatic(
    path.resolve(__dirname, './uploads')
));
app.use(async (ctx, next) => {
    if (ctx.url === '/upload') {
        let file = ctx.request.files.file;
        let filename = path.basename(file.path) + path.extname(file.name);
        fs.renameSync(file.path, path.join(path.dirname(file.path), filename));
        ctx.body = { url: "http://localhost:8080/" + filename };
    } else {
        await next();
    }
});

app.listen(8080, () => {
    console.log('服务器成功在8080端口上启动!');
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值