您当前的位置:首页 > IT编程 > python
| C语言 | Java | VB | VC | python | Android | TensorFlow | C++ | oracle | 学术与代码 | cnn卷积神经网络 | gnn | 图像修复 | Keras | 数据集 | Neo4j | 自然语言处理 | 深度学习 | 医学CAD | 医学影像 | 超参数 | pointnet | pytorch | 异常检测 | Transformers | 情感分类 | 知识图谱 |

自学教程:Python基于React-Dropzone实现上传组件的示例代码

51自学网 2021-10-30 22:23:06
  python
这篇教程Python基于React-Dropzone实现上传组件的示例代码写得很实用,希望能帮到您。

这次我要讲述的是在React-Flask框架上开发上传组件的技巧。我目前主要以React开发前端,在这个过程中认识到了许多有趣的前端UI框架——React-BootstrapAnt DesignMaterial UIBulma等。而比较流行的上传组件也不少,而目前用户比较多的是jQuery-File-UploadDropzone,而成长速度快的新晋有Uppyfilepond。比较惋惜的是Fine-Uploader的作者自2018年后就决定不再维护了,原因作为后来者的我就不多过问了,但请各位尊重每一位开源作者的劳动成果。

这里我选择React-Dropzone,原因如下:

  • 基于React开发,契合度高
  • 网上推荐度高,连Material UI都用他开发上传组件
  • 主要以 Drag 和 Drop 为主,但是对于传输逻辑可以由开发者自行设计。例如尝试用socket-io来传输file chunks。对于node全栈估计可行,但是我这里使用的是Flask,需要将Blob转ArrayBuffer。但是如何将其在Python中读写,我就没进行下去了。

实例演示

1. axios上传普通文件:

通过yarn将react-dropzone和引入:

yarn add react-dropzone axios

前端js如下(如有缺失,请自行修改):

import React, {     useState,     useCallback,    useEffect,} from 'react';import {useDropzone} from 'react-dropzone';import "./dropzone.styles.css"import InfiniteScroll from 'react-infinite-scroller';import {    List,    message,    // Avatar,    Spin,} from 'antd';import axios from 'axios';/*** 计算文件大小* @param {*} bytes * @param {*} decimals * @returns */function formatBytes(bytes, decimals = 2) {    if (bytes === 0) return '0 Bytes';    const k = 1024;    const dm = decimals < 0 ? 0 : decimals;    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];    const i = Math.floor(Math.log(bytes) / Math.log(k));    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];}/*** Dropzone 上传文件* @param {*} props * @returns */function DropzoneUpload(props) {    const [files, setFiles] = useState([])    const [loading, setLoading] = useState(false);    const [hasMore, setHasMore] = useState(true);    const onDrop = useCallback(acceptedFiles => {        setLoading(true);        const formData = new FormData();        smallFiles.forEach(file => {            formData.append("files", file);        });        axios({            method: 'POST',            url: '/api/files/multiplefiles',            data: formData,            headers: {                "Content-Type": "multipart/form-data",            }        })        then(resp => {            addFiles(acceptedFiles);            setLoading(false);        });    }, [files]);    // Dropzone setting    const { getRootProps, getInputProps } = useDropzone({        multiple:true,        onDrop,    });    // 删除附件    const removeFile = file => {        const newFiles = [...files]        newFiles.splice(newFiles.indexOf(file), 1)        setFiles(newFiles)    }    useEffect(() => {        // init uploader files        setFiles([])    },[])    return (        <section className="container">        <div {...getRootProps({className: 'dropzone'})}>            <input {...getInputProps()} />            <p>拖动文件或点击选择文件😊</p>        </div>                <div className="demo-infinite-container">            <InfiniteScroll                initialLoad={false}                pageStart={0}                loadMore={handleInfiniteOnLoad}                hasMore={!loading && hasMore}                useWindow= {false}            >                <List                    dataSource={files}                    renderItem={item=> (                        <List.Item                             actions={[                                // <a key="list-loadmore-edit">编辑</a>,                                 <a key="list-loadmore-delete" onClick={removeFile}>删除</a>                            ]}                            // extra={                                                            // }                            key={item.path}>                            <List.Item.Meta                                 avatar={                                    <>                                    {                                        !!item.type && ['image/gif', 'image/jpeg', 'image/png'].includes(item.type) &&                                        <img                                             width={100}                                            alt='logo'                                            src={item.preview}                                        />                                    }                                    </>                                }                                title={item.path}                                description={formatBytes(item.size)}                            />                        </List.Item>                    )}                >                    {loading && hasMore && (                        <div className="demo-loading-container">                            <Spin />                        </div>                    )}                </List>            </InfiniteScroll>        </div>        </section>    );}

flask代码:

def multiplefiles():if 'files' not in request.files:    return jsonify({'message': '没有文件!'}), 200files = request.files.getlist('files')for file in files:    if file:        # 通过拼音解决secure_filename中文问题        filename = secure_filename(''.join(lazy_pinyin(file.filename))        Path(UPLOAD_FOLDER + '/' + file_info['dir_path']).mkdir(parents=True, exist_ok=True)        file.save(os.path.join(UPLOAD_FOLDER + '/' + file_info['dir_path'], filename))return jsonify({'message': '保存成功!!'})

2. 大文件导入:

通过file.slice()方法生成文件的chunks。不要用Promise.all容易产生非顺序型的请求,导致文件损坏。

js代码:

const promiseArray = largeFiles.map(file => new Promise((resolve, reject) => {                            const chunkSize = CHUNK_SIZE;    const chunks = Math.ceil(file.size / chunkSize);    let chunk = 0;    let chunkArray = new Array();    while (chunk <= chunks) {        let offset = chunk * chunkSize;        let slice = file.slice(offset, offset+chunkSize)        chunkArray.push([slice, offset])        ++chunk;    }    const chunkUploadPromises = (slice, offset) => {        const largeFileData = new FormData();        largeFileData.append('largeFileData', slice)        return new Promise((resolve, reject) => {            axios({                method: 'POST',                url: '/api/files/largefile',                data: largeFileData,                headers: {                    "Content-Type": "multipart/form-data"                }            })            .then(resp => {                console.log(resp);                resolve(resp);            })            .catch(err => {                reject(err);            })        })    };    chunkArray.reduce( (previousPromise, [nextChunk, nextOffset]) => {        return previousPromise.then(() => {            return chunkUploadPromises(nextChunk, nextOffset);        });    }, Promise.resolve());    resolve();}))

flask代码:

filename = secure_filename(''.join(lazy_pinyin(filename)))Path(UPLOAD_FOLDER + '/' + file_info['dir_path']).mkdir(parents=True, exist_ok=True)save_path = os.path.join(UPLOAD_FOLDER + '/' + file_info['dir_path'], filename)# rm file if existsif offset == 0 and save_path.exists(filename):    os.remove(filename)try:    with open(save_path, 'ab') as f:        f.seek(offset)        f.write(file.stream.read())        print("time: "+ str(datetime.now())+" offset: " + str(offset))except  OSError:    return jsonify({'Could not write to file'}), 500

结语

文件传输一直都是HTTP的痛点,尤其是大文件传输。最好的方式是自己做个Client,通过FTP和FTPS的协议进行传输。第二种来自于大厂很中心化的方法,通过文件的checksum来确定文件是否已经上传了,来营造秒传的效果。第三种来自去中心化的Bittorrent的方法每一个用户做文件种子,提供文件传输的辅助,目前国内并没有普及使用。

到此这篇关于Python基于React-Dropzone实现上传组件的示例代码的文章就介绍到这了,更多相关Python React-Dropzone上传组件内容请搜索51zixue.net以前的文章或继续浏览下面的相关文章希望大家以后多多支持51zixue.net!


Python无参装饰器的实现方案及优化
python imread函数详解
万事OK自学网:51自学网_软件自学网_CAD自学网自学excel、自学PS、自学CAD、自学C语言、自学css3实例,是一个通过网络自主学习工作技能的自学平台,网友喜欢的软件自学网站。