• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    Python基于React-Dropzone实现上传组件的示例代码

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

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

    实例演示

    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': '没有文件!'}), 200
    files = 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 exists
    if 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上传组件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    您可能感兴趣的文章:
    • python 实现上传图片并预览的3种方法(推荐)
    • 用Python实现一个简单的能够上传下载的HTTP服务器
    • python实现上传下载文件功能
    • Python selenium文件上传方法汇总
    • python实现的简单FTP上传下载文件实例
    • Python+django实现文件上传
    • python3 flask实现文件上传功能
    • Python基于FTP模块实现ftp文件上传操作示例
    上一篇:Python无参装饰器的实现方案及优化
    下一篇:python imread函数详解
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    Python基于React-Dropzone实现上传组件的示例代码 Python,基于,React-Dropzone,实现,