编程开发 购物 网址 游戏 小说 歌词 快照 开发 股票 美女 新闻 笑话 | 汉字 软件 日历 阅读 下载 图书馆 编程 China
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
移动开发 架构设计 编程语言 互联网 开发经验 Web前端 开发总结
开发杂谈 系统运维 研发管理 数据库 云 计 算 Java开发
VC(MFC) Delphi VB C++(C语言) C++ Builder 其它开发语言 云计算 Java开发 .Net开发 IOS开发 Android开发 PHP语言 JavaScript
ASP语言 HTML(CSS) HTML5 Apache MSSQL数据库 Oracle数据库 PowerBuilder Informatica 其它数据库 硬件及嵌入式开发 Linux开发资料
  编程开发知识库 -> 开发经验 -> form配合iframe实现文件异步上传 -> 正文阅读
 

[开发经验]form配合iframe实现文件异步上传[第1页]

摘要: form配合iframe实现文件异步上传
简述

使用form和iframe实现异步上传文件的功能

原理

form表单可以上传文件,但是提交form表单会打开新页面或刷新页面,为了不刷新页面,将form指向一个隐藏的iframe,form提交后它所指向的iframe会重新加载,iframe的内容即为服务器返回的内容。

优点

兼容各种浏览器各个版本的文件异步上传

缺点

如果要上传的文件需要校验(类型,大小),对于无法获取files属性的浏览器而言,用户体验性不好。

文件需要校验(类型,大小)的处理

能够获取到files就在前台校验,否则后台校验。

示例代码 HTML

<input type="file" name="file" />

JavaScript

$(function(){
    /* 监听change事件 */
    $('input[name=file]').on('change', function(e){
        
        var $target = $(e.target);
        
        /* 获取/创建 iframe */
        var $frame = $('iframe[name=upload_frame]');
        if(!$frame.length){
            $frame = $('<iframe>', {name: 'upload_frame', style: 'display: none;'});
        }
        
        /* 创建form */
        var $form = $('<form>', {
            method: 'POST',                 // post方式提交
            action: 'sever_address',        // 文件上传的服务器地址
            target: 'upload_frame',         // 指向iframe
            enctype:'multipart/form-data'   // 向服务器发送二进制数据
        });
        
        /* 嵌套form */
        $target.wrap($form)
        
        /* 提交表单 */
        $target.parent().submit();
        
        /* 处理回调 */
        $frame.on('load', function(){
            /* 去除嵌套的form */
            $target.parent().replaceWith($target);
            
            /* 接收服务器返回的数据 */
            var resp = $frame.contents().text();
            try {
                resp = JSON.parse(resp);
                /* 提示上传成功?按理说接下的代码才是真正的回调 */
            } catch (error) {
                console.error(error);
            }
        });
    });
});


完整的示例 HTML

<input type="file" name="file" />

JavaScript

$(function(){
    var utils = new this.Utils();
    $('input[type=file]').on('change', function(e){
        utils.uploadQiNiu({
            $target: $(e.target),       # 文件选择器
            prefix: 'subjective/',      # 前缀
            limitSize: 5,               # 限制文件最大为5M
            limitType: ['png', 'jpg'],  # 限制文件类型,
            callback: function(uri){
                utils.showMsg('上传成功: ' + uri);
            }
        });
    });  
})


后台代码 (Python方式实现)

def upload_file(request):

    # 返回信息
    def render(msg_='上传失败', uri_='', name_='', type_=''):
        return HttpResponse(
            content=json.dumps({
                'uri': uri_,
                'msg': msg_,
                'name': name_,
                'type': type_,
            }),
            content_type="text/html;charset=utf-8",
            status=200
        )
        
    # 获取文件
    file_obj = request.FILES.get('file', None)

    # 无文件对象的处理
    if not file_obj:
        return render("请选择文件!")

    post_vars = request.POST

    file_type = file_obj.content_type
    # 判断是否需要验证(类型,大小)
    if post_vars.get('validate', False):
        
        # 验证文件大小
        limit_size = int(post_vars.get('limitSize', 0))
        if limit_size > 0 and file_obj.size > limit_size * 1024 ** 2:
            return render('文件大小不能超过{}M!'.format(limit_size))

        # 验证文件类型
        limit_type = post_vars.get('limitType', '')
        reg = re.compile(file_type, re.I)
        if limit_type and not reg.match(limit_type.replace(',', '|')):
            return render('类型不符, 请上传{}格式的文件!'.format(limit_type))

        # 验证文件后缀
        suffix_type = post_vars.get('suffixType', '')
        if suffix_type:
            file_type = file_obj.name.split('.').pop()
            reg = re.compile(file_type, re.I)
            if not reg.match(suffix_type.replace(',', '|')):
                return render('类型不符, 请上传{}格式的文件!'.format(suffix_type))

    filename = file_obj.name
    prefix = post_vars.get('prefix', '')
    timestamp = int(time.time())
    key = '{}{}{}'.format(prefix, timestamp, filename)

    # 获取配置信息
    access_key = settings.QINIU_ACCESS_KEY
    secret_key = settings.QINIU_SECRET_KEY
    bucket = settings.QINIU_CMS_RESOURCE_BUCKET
    host = settings.QINIU_CMS_RESOURCE_URL

    # 获取上传凭证
    token = Auth(access_key, secret_key).upload_token(bucket, key, 7200)

    # 上传到七牛
    try:
        ret, info = put_data(
            token, key, file_obj.read(), mime_type=file_obj.content_type
        )
        uri = '{}/{}'.format(host, ret['key'])
        msg = '上传成功'
    except Exception as ex:
        logging.error(ex)
        uri = ''
        msg = '上传失败'

    return render(msg, uri, filename, file_type)

JavaScript 提取的公共方法 utils.js

// Generated by CoffeeScript 1.12.4
(function() {
  var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

  this.Utils = (function() {
    function Utils() {
      this.bodyLock = bind(this.bodyLock, this);
      this.showLoading = bind(this.showLoading, this);
      this.confirmMsg = bind(this.confirmMsg, this);
      this.showMsg = bind(this.showMsg, this);
      this.uploadQiNiu = bind(this.uploadQiNiu, this);
    }

    Utils.prototype.uploadQiNiu = function(options) {
      var $form, $frame, $target, file, fileType, files, limitSize, limitType, settings, suffixType;
      settings = {
        $target: null,
        prefix: 'project/',
        limitSize: 0,
        limitType: [],
        suffixType: [],
        callback: (function(_this) {
          return function() {};
        })(this)
      };
      $.extend(settings, options);
      $target = settings.$target;
      if (!$target) {
        console.error('Missing "$target" in settings');
        return false;
      }
      files = $target[0].files;
      if (files) {
        file = files[0];
        if (file == null) {
          this.showMsg('请选择文件!');
          return false;
        }
        limitSize = parseFloat(settings.limitSize);
        if (limitSize > 0 && file.size > limitSize * 1024 * 1024) {
          this.showMsg("文件大小不能超过" + limitSize + "M!");
          $target.val('');
          return false;
        }
        limitType = settings.limitType;
        fileType = file.type.toLowerCase();
        if (limitType.length && !fileType.match(eval("/" + (limitType.join('|')) + "/"))) {
          this.showMsg("类型不符, 请上传" + limitType + "格式的文件!");
          $target.val('');
          return false;
        }
        suffixType = settings.suffixType;
        if (suffixType.length) {
          fileType = file.name.split('.').pop().toLowerCase();
          if (!fileType.match(eval("/" + (suffixType.join('|')) + "/"))) {
            this.showMsg("类型不符, 请上传" + suffixType + "格式的文件!");
            $target.val('');
            return false;
          }
        }
      }
      this.showLoading('正在上传...');
      $frame = $('iframe[name=upload_frame]');
      if (!$frame.length) {
        $frame = $('<iframe>', {
          name: 'upload_frame',
          style: 'display: none'
        });
        $(document.body).append($frame);
      }
      $frame.on('load', (function(_this) {
        return function() {
          var resp;
          _this.bodyLock(false);
          $target.parent().replaceWith($target);
          resp = $frame.contents().text();
          if (resp.indexOf('413 Request Entity Too Large') !== -1) {
            _this.showMsg('上传文件过大!');
            console.error('413 Request Entity Too Large');
            return;
          }
          resp = JSON.parse(resp);
          _this.showMsg(resp.msg);
          return settings.callback(resp.uri, resp.name, (files ? fileType : resp.type), resp.msg);
        };
      })(this));
      $target.attr('name', 'file');
      $form = $('<form>', {
        method: "post",
        name: "upload_form",
        target: "upload_frame",
        action: "/qi_niu_upload/",
        enctype: "multipart/form-data",
        style: 'display:none'
      });
      $target.wrap($form);
      $target.after("<input type='hidden' name='prefix' value='" + settings.prefix + "'>");
      if (!files) {
        $target.after("<input type='hidden' name='validate' value='True'>");
        $target.after("<input type='hidden' name='limitSize' value='" + settings.limitSize + "'>");
        $target.after("<input type='hidden' name='limitType' value='" + settings.limitType + "'>");
        $target.after("<input type='hidden' name='suffixType' value='" + settings.suffixType + "'>");
      }
      $target.parent().submit();
    };

    Utils.prototype.showMsg = function(msg, timeout) {
      if (timeout == null) {
        timeout = 1000;
      }
      return $.jBox.tip(msg, '', {
        timeout: timeout
      });
    };

    Utils.prototype.confirmMsg = function(msg, fun) {
      return $.jBox.confirm(msg, '提示', (function(_this) {
        return function(v) {
          if (v === 'ok') {
            return fun();
          }
        };
      })(this));
    };

    Utils.prototype.showLoading = function(msg) {
      $.jBox.tip(msg, 'loading');
      $('.jbox-fade').css('height', ($(document).height()) + "px");
      return this.bodyLock(true);
    };

    Utils.prototype.bodyLock = function(lock) {
      var body, flag;
      body = $('body');
      flag = lock ? 'hidden' : 'auto';
      return body.css('overflow', flag);
    };

    return Utils;

  })();

}).call(this);


utils.js 对应的源文件 CoffeeScript

class @Utils

    # 上传文件
    uploadQiNiu: (options) =>
        settings =
            $target: null       # 文件选择器
            prefix: 'project/'  # 七牛空间前缀
            limitSize: 0        # 限制大小
            limitType: []       # 文件类型
            suffixType: []      # 后缀类型
            callback: () =>     # 回调函数

        # 配置信息
        $.extend settings, options

        # 判断文件选择器是否存在
        $target = settings.$target
        if not $target
            console.error 'Missing "$target" in settings'
            return false

        files = $target[0].files
        # 能获取到files属性
        if files
            file = files[0]

            # 判断是否选中文件
            if not file?
                @showMsg '请选择文件!'
                return false

            # 判断是否需要限制文件大小
            limitSize = parseFloat settings.limitSize
            if limitSize > 0 and file.size >  limitSize * 1024 * 1024
                @showMsg "文件大小不能超过#{limitSize}M!"
                $target.val ''
                return false

            # 判断是否需要限制文件类型
            limitType = settings.limitType
            fileType = file.type.toLowerCase()
            if limitType.length and not fileType.match eval "/#{limitType.join '|'}/"
                @showMsg "类型不符, 请上传#{limitType}格式的文件!"
                $target.val ''
                return false

            # 判断是否需要限制文件后缀
            suffixType = settings.suffixType
            if suffixType.length
                fileType = file.name.split('.').pop().toLowerCase()
                if not fileType.match eval "/#{suffixType.join '|'}/"
                    @showMsg "类型不符, 请上传#{suffixType}格式的文件!"
                    $target.val ''
                    return false

        @showLoading '正在上传...'
        $frame = $ 'iframe[name=upload_frame]'
        if not $frame.length
            $frame = $ '<iframe>',
                name: 'upload_frame'
                style: 'display: none'
            $(document.body).append $frame


        $frame.on 'load', () =>
            @bodyLock false
            $target.parent().replaceWith($target)

            resp = $frame.contents().text()
            if resp.indexOf('413 Request Entity Too Large') != -1
                @showMsg '上传文件过大!'
                console.error('413 Request Entity Too Large')
                return

            resp = JSON.parse resp
            @showMsg resp.msg
            settings.callback resp.uri, resp.name, (if files then fileType else resp.type), resp.msg

        # 后台获取文件的key为file
        $target.attr 'name', 'file'

        $form = $ '<form>',
            method: "post"
            name: "upload_form"
            target: "upload_frame"
            action: "/qi_niu_upload/"
            enctype: "multipart/form-data"
            style: 'display:none'

        # 外层包装form
        $target.wrap($form)
        $target.after("<input type='hidden' name='prefix' value='#{settings.prefix}'>")
        if not files
            $target.after("<input type='hidden' name='validate' value='True'>")
            $target.after("<input type='hidden' name='limitSize' value='#{settings.limitSize}'>")
            $target.after("<input type='hidden' name='limitType' value='#{settings.limitType}'>")
            $target.after("<input type='hidden' name='suffixType' value='#{settings.suffixType}'>")
        $target.parent().submit()
        return

    # 提示信息
    showMsg: (msg, timeout=1000) =>
        $.jBox.tip msg, '', {timeout: timeout}

    # 确认窗口
    confirmMsg: (msg, fun) =>
        $.jBox.confirm msg, '提示', (v) =>
            if v is 'ok'
                fun()

    # 加载中
    showLoading: (msg) =>
        $.jBox.tip msg, 'loading'
        $('.jbox-fade').css 'height', "#{$(document).height()}px"
        @bodyLock true

    # 设置body是否可以滚动
    bodyLock: (lock) =>
        body = $ 'body'
        flag = if lock then 'hidden' else 'auto'
        body.css 'overflow', flag


  开发经验 最新文章
Java 9 中的 GC 调优基础
Java9之HttpClientAPI实战详解
TeamFlowy——结合Teambition与Workflowy
深度思考Spark Runtime机制
jQuery实现放大镜效果
疯狂Spring Cloud连载(六)——负载均衡框
JavaWeb学习总结——JavaMail发送邮件
GZIP压缩解压类
git常用命令
appach 和tomcat 之间的关系(1)
上一篇文章      下一篇文章      查看所有文章
加:2017-10-30 04:05:34  更:2017-10-30 04:08:21 
VC(MFC) Delphi VB C++(C语言) C++ Builder 其它开发语言 云计算 Java开发 .Net开发 IOS开发 Android开发 PHP语言 JavaScript
ASP语言 HTML(CSS) HTML5 Apache MSSQL数据库 Oracle数据库 PowerBuilder Informatica 其它数据库 硬件及嵌入式开发 Linux开发资料
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 新闻资讯 小游戏 Chinese Culture 股票 三丰软件 开发 中国文化 网文精选 阅读网 看图 日历 万年历 2018年10日历
2018-10-18 18:59:38
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  编程开发知识库