美文网首页
python批量上传

python批量上传

作者: justonlyyo | 来源:发表于2018-09-27 15:42 被阅读0次

    环境

    • python 3.6 你换成其他3x的版本也没关系
    • flask

    项目很小,主要是演示一下使用flask接收页面上传的文件的方法。项目包含一个批量示范页面。

    演示页面文件,为了方便演示。我把html,css和js写在一起了

    演示页面 demo.html

    <!DOCTYPE html>
    <html lang="zh-cn">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0">
        <link rel="stylesheet" href="/static/css/bootstrap.min.css"> <!--bootstrap样式,请自行下载,版本v3-->
        <script src="/static/js/jquery-3.2.1.min.js"></script>  <!--jquery脚本,请自行下载,版本不要求-->
        <script src="/static/js/bootstrap.min.js"></script>   <!--bootstrap脚本,请自行下载,版本v3-->    
        <style>
        body {
          font-size: 14px;
        }
        .my_div {
      min-height: 400px;
      background-color: #f7f7f7;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
    }
    .my_div .json_div {
      min-width: 100%;
      padding: 10px;
    }
    .my_div .json_div >.json_div_inner {
      min-width: 100%;
      min-height: 50px;
      border: 1px solid #d3d3d3;
      border-radius: 5px;
      padding: 10px;
    }
    .my_div .json_div >.json_div_inner >li {
      margin-left: 20px;
    }
    .my_div #my_progress {
      width: 96%;
      position: relative;
    }
    .my_div #my_progress >.my_per {
      color: #ff7d7f;
      position: absolute;
      display: block;
      width: 4em;
      top: 1px;
      left: 50%;
    }
    .my_div .my_bottom {
      padding: 10px 10px 20px;
    }
    .my_div .my_bottom >#select_image {
      display: none;
    }
    
        </style>
        <title>批量上传演示页</title>
    </head>
    <body>
    
        <div class="container-fluid">
            <div class="row">
                <div class="col-lg-6 col-lg-offset-3 col-md-8 col-md-offset-2 col-sm-10 col-sm-offset-1 col-xs-12 my_div">
                    <div class="json_div">
                        <ul class="json_div_inner"></ul>
                    </div>
                    <div id="my_progress" class="progress">
                      <div class="progress-bar" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 0%;">
    
                      </div>
                        <span class="my_per">0%</span>
                    </div>
                    <div class="my_bottom">
                        <!--
                            <input id="select_image" type="file" accept="image/*" capture="camera" multiple>
                            accept表示打开系统文件目录
                            accept="image/*"  打开相册
                            accept="video/*"  打开视频
                            capture  指调用哪些设备
                            capture='microphone'   调用录音机
                            capture='camera'       调用相机
                            capture='camcorder'    调用摄像机
                            file类型的input还有一个 multiple 的单值属性.表示同时提供打开文件和设备的选项.
                            IOS中, multiple  属性无效. 必须写2个input来自行实现选择.
                            判断是否是苹果手机的方法:
                            navigator.userAgent.toLowerCase() == "iphone os"
                            由于目前拍摄的照片尺寸过大,所以暂时关闭拍照的功能(没有capture="camera" 和 multiple属性)
                        -->
                        <input id="select_image" type="file" multiple size="200">
                        <button id="select_btn" class="btn btn-default btn-primary">选择</button>
                        <button id="upload_btn" class="btn btn-default btn-primary">上传</button>
                    </div>
                </div>
            </div>
    
        </div>
    
    </body>
    <script>
    function upload_progress(event, progress_cb){
        /*
        上传进度处理函数
        :params event:       文件上传事的事件,
        :params progress_cb: 回调函数,本函数会把上传完成的百分数当作地一个参数传入此回调函数.
        默认情况下.会在控制台打印上传完成度. 注意,100并不代表服务端完整的接收到了文件.
        只代表页面已经发送完了所有的文件内容.
        */
        if (event.lengthComputable) {
            var complete_percent = Math.round(event.loaded * 100 / event.total);
            var handler = progress_cb?progress_cb: function(num){console.log(`上传完成度:${num}`)};
            handler(complete_percent);
        }else{}
    }
    
    function upload_complete(event, success_cb){
        /*
        上传文件success时的事件,只要服务器返回状态码200,就会执行本函数,并并不是代表服务器返回了正确的信息.
        根据实际需要可以覆盖.
        :params event: 文件上传事的事件,一般由XMLHttpRequest的upload的事件监听器来传递事件.
        :params success_cb:   成功时的回调函数,
        :return: nothing
        */
        let str = event.target.responseText;
        let handler = success_cb? success_cb: function(a){console.log(a);};
        handler(str);
    }
    
    function upload_error(event, error_cb){
        /*
        上传文件失败时的事件,根据实际需要可以覆盖.
        :params event: 文件上传事的事件,一般由XMLHttpRequest的upload的事件监听器来传递事件.
        :params error_cb: 失败时的回调函数,
        :return: nothing
        */
        let handler = error_cb? error_cb: function(a){console.log(event);};
        handler(event);
    }
    
    function batch_upload(options){
        /*
        批量上传文件. 不限制文件大小
        options = {
        files: 数据的序列,
        url: str,
        headers: 键值对对象,
        success_cb: function,
        error_cb: function,
        progress_cb: function,
        }
        :params files:        input标签的files
        :params url:          上传的服务器url
        :params headers:      放入header的参数,是键值对形式的字典,键名不要用下划线,因为那不符合规范
        :params success_cb:   成功时的回调函数,会把服务器的返回信息作为第一个参数传入此回调函数.
        :params error_cb:     失败时的回调函数,会把错误信息作为第一个参数传入此回调函数.
        :params progress_cb:  上传时的返回上传进度的回调函数,会把页面上传文件的百分书作为第一个参数传入此回调函数..
        :return:              不返回数据,由回调函数返回.
        有关XMLHttpRequest对象的详细信息,请参考.
        https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest
        有关XMLHttpRequest.send方法的详细文档地址:
        https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/send
        */
        let files = options['files'];
        let file_data = options['file_data'];
        let url = options['url'];
        let headers = options['headers'];
        let success_cb = options['success_cb'];
        let error_cb = options['error_cb'];
        let progress_cb = options['progress_cb'];
        let prog_func = function(event){upload_progress(event, progress_cb)};  // 进度的回调函数
        let comp_func = function(event){upload_complete(event, success_cb)};  // 成功时的回调函数
        let erro_func = function(event){upload_error(event, error_cb)};  // 失败时的回调函数
    
        // 构造数据容器
        let data = new FormData();
        for(let file of files){
            data.append(file.name, file);
        }
        // 新建一个请求对象
        let req = new XMLHttpRequest();
        // 添加事件监听器
        req.upload.addEventListener("progress", prog_func, false);
        req.addEventListener("load", comp_func, false);
        req.addEventListener("error", erro_func, false);
        req.addEventListener("abort", erro_func, false);
        req.open("post", url);
        // 必须在open之后才能给请求头赋值
        if(headers){
            /*
            * 传送请求头信息,目前服务端还未做对应的处理.这只是与被给后来使用的.
            * */
            for(let k in headers){
                req.setRequestHeader(k, headers[k]);
            }
        }
        try{
            req.send(data);  // 404错误会直接在此抛出
        }catch(e){
            let handler = error_cb? error_cb: function(ms){console.log(ms);};
            handler(e);
        }
    
    }
    
    /*扩展函数注册区域*/
    
    $.extend({
        batch_upload: batch_upload                              // 批量上传文件,无尺寸限制
    });
    
    // 点击选择按钮的事件
        $("#select_btn").click(function(){
            $("#select_image").click();
        });
    
        let progress = function(per){
            // 处理上传进度
            console.log(`per is ${per}`);
            $("#my_progress>.progress-bar").attr("style", `width: ${per}%;`).attr("aria-valuenow", `${per}`);
            if(per > 52){
                $(".my_per").css("color", "#fff");
            }
            $(".my_per").text(`${per}%`);
        };
    
        // 上传图片按钮事件
        $("#select_image").change(function(){
            let ul = $(".json_div_inner:first");
            ul.empty();
            let files = this.files;
            for(let f of files){
                ul.append(`<li id="${f.name}"><span>${f.name}</span></li>`);
            }
        });
        let call_back = function(){
            $("#upload_btn, #select_btn").attr("disabled", false).removeClass("disabled");
            alert("操作结束!");
        };
    
        // 上传图片模态框,提交按钮事件
        $("#upload_btn").click(function(){
                let $obj = $("#select_image");
                let files = $obj[0].files;
                let opts = {
                    "url": "/file/save",
                    "files": files,
                    "success_cb": call_back,
                    "error_cb": call_back,
                    "progress_cb": progress
                };
                $("#upload_btn, #select_btn").attr("disabled", true).addClass("disabled");
                $.batch_upload(opts);
            });
    </script>
    </html>
    

    服务端的文件也很简单 server.py

    # -*- coding: utf-8 -*-
    from flask import Flask
    from flask import render_template
    from flask import request
    import json
    import os
    
    
    app = Flask(__name__)
    port = 7002
    root_dir = os.path.dirname(os.path.realpath(__file__))
    resource_dir = os.path.join(root_dir, 'resource')
    
    @app.route("/")
    def upload_demo():
        """上传页面"""
        return render_template("upload_demo.html", page_title="批量上传")
    
    
    @app.route("/file/<action>", methods=['post', 'get'])
    def file_func(action):
        """
        :param action: 动作, save/get(保存/获取)
        :return:
        """
        mes = {"message": "success"}
        if action == "save":
            """保存文件"""
            if os.path.exists(resource_dir):
                pass
            else:
                os.makedirs(resource_dir)
    
            for key_name, file_storage in request.files.items():
                if file_storage is not None:
                    file_name = file_storage.filename
                    file_storage.save(os.path.join(resource_dir, file_name))
                    file_storage.close()
        elif action == "get":
            """获取文件/图片,未实现"""
            pass
        return json.dumps(mes)
    
    if __name__ == '__main__':
        app.run(host="0.0.0.0", port=port, debug=True, threaded=True)
    

    项目的目录结构也很简单。


    2018-09-27 15-40-54屏幕截图.png

    相关文章

      网友评论

          本文标题:python批量上传

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