HTML5-WebSocket实现多文件同时上传

在传统的HTTP应用上传文件想要同时上传多个文件并查看上传进度是一件很麻烦的事情,当然现在也有一些基于SWF的文件上传组件提供这种的便利性.到了HTML5下对文件的读取和上传的控制方面就非常灵活,HTML5提供一系列的AIP进行文件读取,包括计取文件某一块的内容也非常方便,结合Websocket进行文件的传输就变得更加方便和灵活.下面通过使用HTML5结合websocet简单地实现多文件同时上传应用。

实现功能


大概预览一下需要做的功能:



主要功能是用户可以直接把文件夹的文件直接拖放到网页中,并进行上传,在上传的过程中显示上传进度信息.


FileInfo类封装


为了方便读取文件信息,在原有File的基础封装了一个简单文件信息读取的对象类.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
function  FileInfo(file, pagesize) {
     this .Size = file.size;
     this .File = file;
     this .FileType = file.type;
     this .FileName = file.name;
     this .PageSize = pagesize;
     this .PageIndex = 0;
     this .Pages = 0;
     this .UploadError =  null ;
     this .UploadProcess =  null ;
     this .DataBuffer =  null ;
     this .UploadBytes = 0;
     this .ID = Math.floor(Math.random() * 0x10000).toString(16);
     this .LoadCallBack =  null ;
     if  (Math.floor( this .Size %  this .PageSize) > 0) {
         this .Pages = Math.floor(( this .Size /  this .PageSize)) + 1;
  
     }
     else  {
         this .Pages = Math.floor( this .Size /  this .PageSize);
  
     }
  
}
FileInfo.prototype.Reset =  function  () {
     this .PageIndex = 0;
     this .UploadBytes = 0;
}
FileInfo.prototype.toBase64String =  function  () {
     var  binary =  ''
     var  bytes =  new  Uint8Array( this .DataBuffer)
     var  len = bytes.byteLength;
  
     for  ( var  i = 0; i < len; i++) {
         binary += String.fromCharCode(bytes[i])
     }
     return  window.btoa(binary);
}
FileInfo.prototype.OnLoadData =  function  (evt) {
     var  obj = evt.target[ "tag" ];
  
     if  (evt.target.readyState == FileReader.DONE) {
         obj.DataBuffer = evt.target.result;
         if  (obj.LoadCallBack !=  null )
             obj.LoadCallBack(obj);
  
     }
     else  {
         if  (obj.UploadError !=  null )
             obj.UploadError(fi, evt.target.error);
     }
  
}
  
FileInfo.prototype.Load =  function  (completed) {
     this .LoadCallBack = completed;
     if  ( this .filereader ==  null  ||  this .filereader == undefined)
         this .filereader =  new  FileReader();
     var  reader =  this .filereader;
     reader[ "tag" ] =  this ;
     reader.onloadend =  this .OnLoadData;
     var  count =  this .Size -  this .PageIndex *  this .PageSize;
     if  (count >  this .PageSize)
         count =  this .PageSize;
     this .UploadBytes += count;
     var  blob =  this .File.slice( this .PageIndex *  this .PageSize,  this .PageIndex *  this .PageSize + count);
  
     reader.readAsArrayBuffer(blob);
};
  
FileInfo.prototype.OnUploadData =  function  (file) {
     var  channel = file._channel;
     var  url = file._url;
     channel.Send({ url: url, parameters: { FileID: file.ID, PageIndex: file.PageIndex, Pages: file.Pages, Base64Data: file.toBase64String()} },  function  (result) {
         if  (result.status ==  null  || result.status == undefined) {
             file.PageIndex++;
             if  (file.UploadProcess !=  null )
                 file.UploadProcess(file);
             if  (file.PageIndex < file.Pages) {
                 file.Load(file.OnUploadData);
             }
         }
         else  {
  
             if  (file.UploadError !=  null )
                 file.UploadError(file, data.status);
         }
     });
}
  
FileInfo.prototype.Upload =  function  (channel, url) {
     var  fi =  this ;
     channel.Send({ url: url, parameters: { FileName: fi.FileName, Size: fi.Size, FileID: fi.ID} },  function  (result) {
         if  (result.status ==  null  || result.status == undefined) {
             fi._channel = channel;
             fi._url = result.data;
             fi.Load(fi.OnUploadData);
         }
         else  {
             if  (file.UploadError !=  null )
                 file.UploadError(fi, result.status);
         }
     });
  
}


类的处理很简单,通过file初始化并指定分块大小来实始化一些文件信息,如页数量页大小等.当然最重要还封装文件对应的Upload方法,用于把文件块信息打包成base64信息通过Websocket的方式发送到服务器。


文件拖放


在HTML5中接受系统文件拖放进来并不需要做复杂的事情,只需要针对容器元素绑定相关事件即可.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function  onDragEnter(e) {
             e.stopPropagation();
             e.preventDefault();
         }
  
         function  onDragOver(e) {
             e.stopPropagation();
             e.preventDefault();
             $(dropbox).addClass( 'rounded' );
         }
  
         function  onDragLeave(e) {
             e.stopPropagation();
             e.preventDefault();
             $(dropbox).removeClass( 'rounded' );
         }
  
         function  onDrop(e) {
             e.stopPropagation();
             e.preventDefault();
             $(dropbox).removeClass( 'rounded' );
             var  readFileSize = 0;
             var  files = e.dataTransfer.files;
             if  (files.length > 0) {
                 onFileOpen(files);
             }
  
         }


只需要在onDrop过程中获取相关拖放文件即可,这些可能通过一些HTML5的教程可以得到帮助,详细看http://www.html5rocks.com/zh/tutorials/file/dndfiles/


这时候只需要针对选择的文件构建相关FileInfo对象,并调用上传方法即可.


1
2
3
4
5
6
7
8
9
10
function  onFileOpen(files) {
             if  (files.length > 0) {
                 for  ( var  i = 0; i < files.length; i++) {
                     var  info =  new  FileInfo(files[i], 32768);
                     uploads.push(info);
                     info.UploadProcess = onUploadProcess;
                     addUploadItem(info);
                 }
             }
         }


通过UploadProcess事件对上传文件进度信息进行一个设置更新


1
2
3
4
5
function  onUploadProcess(file) {
             $( '#p_'  + file.ID).progressbar({ value: (file.PageIndex / file.Pages) * 100,
                 text: file.FileName +  '['  + file.UploadBytes +  '/'  + file.Size +  ']'
             });
         }


C#服务端


借助于Beetle对websocket的支持对应服务端的实现就非常简单了


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/// <summary>
     /// Copyright © henryfan 2012        
     ///Email:   henryfan@msn.com    
     ///HomePage:    http://www.ikende.com       
     ///CreateTime:  2012/12/14 21:13:34
     /// </summary>
     public  class  Handler
     {
         public  void  UploadPackage( string  FileID,  int  PageIndex,  int  Pages,  string  Base64Data)
         {
             Console.WriteLine( "FileID:{2},PageIndex:{0} Pages:{1} DataLength:{3}" , PageIndex, Pages, FileID,Base64Data.Length);
  
         }
         public  string  UploadFile( string  FileID,  string  FileName,  long  Size)
         {
             Console.WriteLine( "FileID:{2},FileName:{0} Size:{1}" , FileName, Size, FileID);
             return  "Handler.UploadPackage" ;
         }
     }


服务端方法有两个一个是上传文件请求,和一个上传文件块接收方法。


总结


只需要以上简单的代码就能实现多文件同时上传功能,在这采用json来处理上传的信息,所以文件流要进行一个base64的编码处理,由于websocket浏览提交的数据一般都有MASK处理再加上base64那损耗相对来说比较重,实际上websocket有提供流的数据包格式(arraybuffer);当然这种处理在操作上就没有json来得方便简单。



演示地址:http://html5.ikende.com/upload.htm 使用chrome或IE10浏览器


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值