Zen Space
php erlang javascript ruby python web linux mac os x
rss
email
twitter
facebook
  • Labs
  • Resume
  • Works
  • About

让你的网站也像Gmail一样支持文件拖放上传-HTML5之File API

12 comments
Posted on 四 25 2010 by reeze

如果你比较好奇,可以先从这里下载所有代码,也可以点击这里查看chrome下上传的demo,点这里查看firefox下的demo

前不久Gmail推出了支持拖拽的附件上传功能,试用了下还真不错,其实很久以前就在想能有直接拖拽附件的功能,多亏有了HTML5,Web应用越来越像客户端的应用了。

在好奇心驱使下,想了解一下Gmail到底是怎么做到的,了解了一下最新的HTML5 File API草案,这个接口主要提供的就是提供对文件对象的访问,别想歪了,这个接口是无法随意的访问系统里的文件的。他能做的就是访问<input type=”file” />标签里所选择的文件,这些文件可以通过用户手动选择,或者是HTML5的拖放接口选中的文件。有兴趣的童鞋可以看看这个规范,还算比较简单。

下面简单看看接口定义几个对象。

FileList、File对象。

在HTML5中的<input type=”file” />标签中增加了mutilple属性,允许进行多文件选择。大家应该都知道一般上传标签中是不允许选择多个文件的。 新增的这个属性就是允许进行多个文件的选择(这个在桌面应用中也很常见)。<input type=”file” multiple=”multiple” id=”file” />

下面是在Firebug中的输出

1
2
3
4
5
>>> var f = document.getElementById("file")
>>> f.files
FileList0=File length=1 // 选中的文件数量
>>> f.files[0]
FilefileName=es.dll fileSize=271360

FileList对象就是用户选择的所有文件的对象表示,如果是通过input标签选择的,就可以通过上面代码所示的方法进行访问,File对象就可以刚才选择的某个文件的信息,如上面的代码所示,主要可以得到所选中的文件名以及文件大小信息。

你可能在想只能得到这些信息到底有什么用呢?都没有办法读取文件内容,这就得提到规范中的FileReader接口了,这个接口就是用来读取File对象文件的。

在File API规范中提到File API主要是和其他的接口协同合作。比如XMLHttpRequest (这个新接口支持通过xhr的send()方法发送File对象), DataTransfer(也就是HTML5中的拖拽接口 ), 以及Web Worksers(这个主要是异步脚本执行,相当于给JS提供了“多线程”脚本执行能力,并且支持通过postMessage()进行“线程间通信”),感兴趣的,可以看看这篇日志,以及这篇。

目前能实现这样的效果的方式主要有如下几种:

  • Gmail中提到的这两个浏览器都支持拖放接口,托放以后可以直接通过托放事件的DataTransfer属性访问到本次托放是关联的文件对象列表FileList,然后通过XMLHttpRequest的send方法将File对象发送到服务器
  • 在Chrome下支持直接将文件拖放到文件选择控件上,就相当于直接选择了文件。这时可以通过<input type=”file” />DOM对象的files属性访问到被托放进来的文件列表对象,然后也可以通过Ajax将文件对象发送到服务器,通过将文件选择控件透明度降低也可以实现Gmail类似的效果。 在Chrome因为可以直接通过托拽的方式让文件选择控件“赋值”,此时也可以通过一个iframe加表单的方式将数据发送到服务器。
  • 在Firefox3.6下可以通过FileReader直接读取到文件的内容,然后直接将文件内容发送到服务器端(可以参考这个例子,这是个不完整的例子,直接浏览是看不到效果的,查看源代码你就会懂的。)

下面就来看看Gmail到底是怎么做到的吧。

本来想通过Firebug的概况功能来捕捉到在托拽期间的脚本执行情况,比如:


但是脚本执行里压根没有找到ajax相关的函数调用,可能是因为firebug还不支持监控页面里嵌入的iframe中的脚本执行跟踪,这也说明本次上传肯定是在某个iframe中完成的。,那就直接监听网络吧,托拽上传一个附件时查看网络情况,发现附件是通过下面的ajax post过去的:

大家注意看,是通过ajax post方式将附件POST到服务器的,


可以看出Gmail在firefox下不是通过表单直接提交实现的。在chrome下的开发人员工具有点简单,无法看到网络情况,我也懒的再去抓包看了,估计是使用透明<input type=”file” />+ajax方式实现的。

在Gmail支持托拽的声明中提到目前只支持Chrome 2+以及FireFox3.6+。虽然这两个浏览器都支持HTML5,但是对于所有规范的支持程度都是不一样的,并且规范也还不是正式规范。在Firefox3.6的release note中提到:

Support for new DOM and HTML5 specifications including the Drag & Drop API and the File API, which allow for more interactive web pages.

开始支持了HTML5的拖拽接口以及File API。


下面根据浏览器以及HTML5的规范整理出两个浏览器下实现类似Gmail 上传附件的代码。

点击这里下载所有代码,有兴趣的童鞋查看源代码就知道怎么回事了,有一定的注释:)

也可以点击这里查看chrome下上传的demo,点这里查看firefox下的demo,之所以分开是为了简单起见,当然你真的想要给你的网站提供托拽上传功能,你就得自己去同时兼容这两个浏览器啦,相信这也不是件困难的事情:)


  Tags: File API, Gmail, HTML5, 托放上传 Category: HTML5, 未分类

12 Comments

  1. jumkey 说:
    2010年04月28日于11:49 上午

    功能不错,以后方便上传

    回复
  2. jumkey 说:
    2010年04月28日于5:20 下午

    想知道服务器端怎么保存文件呢

    回复
    • reeze 说:
      2010年04月28日于5:45 下午

      我的实例代码中有个handler.php 看到没?那个文件只是简单的将文件内容返回。

      在用ajax发送文件的时候这样写的:
      xhr.setRequestHeader(‘Content-Type’, ‘multipart/form-data’); xhr.setRequestHeader(‘X-File-Name’, files[i].fileName);
      xhr.setRequestHeader(‘X-File-Size’, files[i].fileSize);
      xhr.send(files[i]);

      如果是需要保存的话,可以这样
      < ?php
      $file_name = $_SERVER['X-File-Name'];
      $file_content = file_get_contents('php://input'); // 读取收到的文件内容

      // 写到文件里,这里文件名你可以设置到你需要保存的文件位置里。
      file_put_contents($file_name, $file_content);
      ?>

      回复
      • jumkey 说:
        2010年05月5日于11:23 上午

        谢谢,我是学java的,不过还是不知道java要这么获取send的file。还有$_SERVER['X-File-Name']是不是要写成$_SERVER['HTTP_X_FILE_NAME']啊

        回复
        • reeze 说:
          2010年05月5日于8:20 下午

          呵呵,对的,就该写成$_SERVER['HTTP_X_FILE_NAME'],没有用过java。我还真不知道该怎么用java读文件。HTTP 库应该有方法的。

          回复
  3. bin 说:
    2010年05月21日于3:05 上午

    感觉有些没有写清楚,补充点
    this.xhr = new XMLHttpRequest();//xhr的值
    this.xhr.open(“POST”, xxx.php?name=xxx);//传值的页面
    获取参数$_GET[name]
    不过文件名一直会出现乱码问题,这个我也没有解决

    java下可以使用servlet
    InputStream和FileOutputStream
    就和php中的
    $file_content = file_get_contents(‘php://input’);
    作用类似

    回复
    • jumkey 说:
      2010年05月21日于9:13 上午

      Java的我已经实现了,不过例子中的document.body.innerHTML+=”上传……”有问题的。会导致div绑定的拖拽事件全部失效。可能是dom重写了的缘故。使用appendChild()就没问题了。乱码可以使用js的encodeURI()编码中文,在后台解码就没问题了

      回复
      • bin 说:
        2010年05月21日于10:05 上午

        document.body.innerHTML这个可以使用jQuery来写after(content)

        回复
      • reeze 说:
        2010年05月28日于2:44 下午

        赞,我自己试的时候也出现了托拽处理函数失效的问题呢.
        当时没明白怎么回事~~

        回复
  4. bin 说:
    2010年05月21日于3:14 上午

    我的头像怎么没有了

    回复
  5. windy 说:
    2010年08月9日于1:00 下午

    很感兴趣,我怎么下载不了源代码。

    回复
    • reeze 说:
      2010年08月26日于3:40 下午

      前段时间搬了一次服务器,文件没有到过来,现在好了,还有需要的话,现在可以下的到:)

      回复

Leave a Reply

点击这里取消回复




Spam Protection by WP-SpamFree

最近文章

  • 解决Mac下终端中使用screen管理回话但看不到滚动条等问题
  • 让你的网站也像Gmail一样支持文件拖放上传-HTML5之File API
  • 开启Mac OS X Snow Leopard的NTFS原生读写
  • 在2009的尾巴上
  • 支持IPv6的Tunnelblick For Mac OS X OpenVPN客户端

分类目录

  • G-related (1)
  • Hackintosh (1)
  • HTML5 (1)
  • JavaScript (1)
  • Mac (5)
  • PHP (5)
  • tips (1)
  • 乱78糟 (1)
  • 未分类 (5)

标签~云

2009 debug editor exception File API Gmail html HTML5 JavaScript linux Mac MacFUSE MacOSX NTFS OS X PHP screen screenrc Snow Leopard softwares ssh syntax check Tools VPN 托放上传

一些脚印

  • 张二 在 解决Mac下终端中使用screen管理回话但看不到滚动条等问题 上的评论
  • joe001 在 开启Mac OS X Snow Leopard的NTFS原生读写 上的评论
  • reeze 在 让你的网站也像Gmail一样支持文件拖放上传-HTML5之File API 上的评论
  • windy 在 让你的网站也像Gmail一样支持文件拖放上传-HTML5之File API 上的评论
  • L42y 在 Lockerz邀请 上的评论

Google Reader

    Shared Items

    日志存档

    • 2010年七月 (1)
    • 2010年四月 (1)
    • 2010年一月 (1)
    • 2009年十二月 (3)
    • 2009年十月 (2)
    • 2009年七月 (4)
    • 2009年六月 (4)

    链接表

    • Ideawu
    • LinuxToy
    • on Github
    • Tina的设计天地
    • 风雪之隅
    想读
    围城
    灿烂千阳
    The Laws of Simplicity
    结网
    蔡康永的说话之道
    正读
    建筑的永恒之道
    亲密行为
    裸猿三部曲:人类动物园
    裸猿三部曲:裸猿
    Clean Code
    读过
    我的奋斗
    无懈可击的Web设计
    应用Rails进行敏捷Web开发
    追风筝的人
    构建可扩展的Web站点

    • Resume
    • Works
    • About
    Powered by Wordpress  |  Designed by WebTreats