HTTP协议下用Web Service上传大文件的解决方案
程序员文章站
2023-11-11 21:11:16
用http协议上传大文件也许是个不好办的问题。主要是它的不连续性,使得上传文件感觉很“危险”。特别是很大的文件(几百mb甚至是上g的文件),心里总觉得不踏实,一不小心就会出...
用http协议上传大文件也许是个不好办的问题。主要是它的不连续性,使得上传文件感觉很“危险”。特别是很大的文件(几百mb甚至是上g的文件),心里总觉得不踏实,一不小心就会出现问题,而一但出现问题就无法继续上传,这是很郁闷的。
后来在一些网站上找到一些上传文件的组件,但都是要用到一些com组件。至于后来的asp.net下上传大文件的解决方案,我也做过一个组件,后来发现根本就不用自己写什么组件,利用asp.net自己的上传方法也可以解决大文件上传,真是郁闷的要死了。。。。
回想之后,决定用web service来做一个文件上传,还是利用http协议,这样不用在服务器上做太多的变动,而客户端也简单。
首先是解决方案的设计:因为web service可以利用soap来传递数据,而且可以传递十进制数据,因此可以想到,在服务上公开一个方法,参数可以是byte数组,这样可以把文件分块的上传到服务器。这一解决方法我做过,但速度很慢。后来在ms上找到一些文章,用ms最新公开的服务组件上传文件,速度快了很多。而自己所要做的就是组织一些安全性的问题。
部份代码:upload instance
using system;
using system.io;
using microsoft.web.services2;
using microsoft.web.services2.dime;
namespace webb.wave.winupload
{
/**//// <summary>
/// summary description for controls.
/// </summary>
public class uploadinstance2
{
fields#region fields
private string m_guid;
private datetime m_uploadtime;
private long m_filelength;
private long m_currentpoint;
private string m_pathonserver;
private long m_userid;
#endregion
properties#region properties
public long userid
{
get{return this.m_userid;}
set{this.m_userid=value;}
}
public string guid
{
get{return this.m_guid;}
set{this.m_guid=value;}
}
public datetime uploadtime
{
get{return this.m_uploadtime;}
set{}
}
public long filelength
{
get{return this.m_filelength;}
set{this.m_filelength=value;}
}
public long currentpoing
{
get{return this.m_currentpoint;}
set{this.m_currentpoint=value;}
}
public string pathonserver
{
get{return this.m_pathonserver;}
set{this.m_pathonserver=value;}
}
public string fullpathonserver
{
get
{
if(this.m_guid!=string.empty&&this.m_pathonserver!=string.empty)
{
return path.combine(this.m_pathonserver,this.m_guid+".rem");
}
else
{
return string.empty;
}
}
}
public string filename
{
get
{
if(this.m_guid!=string.empty)
{
return this.m_guid+".rem";
}
else
{
return string.empty;
}
}
}
#endregion
public uploadinstance2()
{
this.m_guid = system.guid.newguid().tostring();
this.m_uploadtime = system.datetime.now;
this.m_currentpoint = 0;
this.m_filelength = 0;
this.m_pathonserver = string.empty;
}
public uploadinstance2(string i_path,string i_guid,long i_filelength)
{
string m_fullpath = path.combine(i_path,i_guid);
if(!file.exists(m_fullpath)) return;
this.m_guid = i_guid;
this.m_uploadtime = system.datetime.now;
this.m_pathonserver = i_path;
fileinfo m_fileinfo = new fileinfo(m_fullpath);
this.m_currentpoint = m_fileinfo.length;
this.m_filelength = i_filelength;
}
public bool uploaddata(byte[] i_data, long i_currentpoint, int i_datasize)
{
string m_fullpath = this.fullpathonserver;
if(!file.exists(m_fullpath)&&this.m_currentpoint!=0)return false;
long m_filepoint = new fileinfo(m_fullpath).length;
if(m_filepoint!=i_currentpoint) return false;
filestream m_filestream = new filestream(m_fullpath,filemode.append);
m_filestream.write(i_data,0,i_datasize);
m_filestream.close();
return true;
}
public void abandantupload()
{
string m_fullpath = this.fullpathonserver;
try{file.delete(m_fullpath);}
catch{}
}
public void createfile()
{
string m_fullpath = this.fullpathonserver;
if(!file.exists(m_fullpath))
{
file.create(m_fullpath).close();
}
else
{
try
{
file.delete(m_fullpath);
}catch{}
file.create(m_fullpath).close();
}
}
}
}
上传过程:
#region uploadprocess
public void uploadprocess()
{
datetime m_start = datetime.now;
this.textbox_outmsg.appendtext("initialize upload\r\n");
if(this.m_upload==null||this.m_uploadguid==null||this.m_uploadguid==string.empty)
{
this.textbox_outmsg.appendtext("upload instance id error or login to the server faild\r\n");
this.textbox_outmsg.appendtext("upload faild.\r\n");
return;
}
this.textbox_outmsg.appendtext("open file\r\n");
if(this.m_filepath==null||this.m_filepath==string.empty||!file.exists(this.m_filepath))
{
this.textbox_outmsg.appendtext("open file error\r\n");
this.textbox_outmsg.appendtext("upload faild.\r\n");
return;
}
fileinfo m_fileinfo = new fileinfo(this.m_filepath);
filestream m_fs = new filestream(this.m_filepath, filemode.open, fileaccess.read);
this.textbox_outmsg.appendtext("start upload file\r\n");
int m_buffer = 10; //kbytes
long m_currentpoint = 0;
long m_filelength = m_fileinfo.length;
bool m_uploadresult = false;
byte[] m_data = new byte[m_buffer*1024];
long m_readbytes = m_fs.read(m_data, 0, m_buffer*1024);
this.uploadprocessbar.maximum = 100;
this.uploadprocessbar.minimum = 0;
while(m_readbytes>0)
{
memorystream m_memorystream = new memorystream(m_data, 0,(int)m_readbytes);
dimeattachment dimeattach = new dimeattachment("image/gif", typeformat.mediatype, m_memorystream);
this.m_upload.requestsoapcontext.attachments.add(dimeattach);
m_uploadresult = this.m_upload.uploadfiledata(this.m_uploadinstance,m_currentpoint,m_readbytes);
if(m_uploadresult)
{
m_currentpoint +=m_readbytes;
m_readbytes = m_fs.read(m_data,0,m_buffer*1024);
// this.textbox_outmsg.appendtext("uploading:"+m_currentpoint.tostring()+"/"+m_filelength.tostring()+"\r\n");
this.uploadprocessbar.value = (int)(m_currentpoint*100/m_filelength);
this.label_outpercent.text = this.uploadprocessbar.value.tostring()+"%";
}
else
{
this.textbox_outmsg.appendtext("upload file error.\r\n");
m_fs.close();
this.m_upload.abandantupload(this.m_uploadinstance);
return;
}
}
this.textbox_outmsg.appendtext("file upload finished.\r\n");
this.button_cancel.enabled = false;
m_fs.close();
this.resetform();
}
#endregion
测试项目代码:
http://test.0579fw.com/myfile/kiyeer/客户上传/webbwinupload.zip
出现错误的解决方法:
*****************************************************
引用内容
error 1 'winformtest.localhost.webbwinupload' does not contain a definition for 'requestsoapcontext' d:\webbwinupload\winformtest\webbwinupload.cs 448 19 winformtest
当你更新web引用的时候,.net自动生成的web引用为:
public class webbwinupload : system.web.services.protocols.soaphttpclientprotocol
请转化为:
public class webbwinupload : microsoft.web.services2.webservicesclientprotocol
查找引用下自动生成的c#文件reference.cs
后来在一些网站上找到一些上传文件的组件,但都是要用到一些com组件。至于后来的asp.net下上传大文件的解决方案,我也做过一个组件,后来发现根本就不用自己写什么组件,利用asp.net自己的上传方法也可以解决大文件上传,真是郁闷的要死了。。。。
回想之后,决定用web service来做一个文件上传,还是利用http协议,这样不用在服务器上做太多的变动,而客户端也简单。
首先是解决方案的设计:因为web service可以利用soap来传递数据,而且可以传递十进制数据,因此可以想到,在服务上公开一个方法,参数可以是byte数组,这样可以把文件分块的上传到服务器。这一解决方法我做过,但速度很慢。后来在ms上找到一些文章,用ms最新公开的服务组件上传文件,速度快了很多。而自己所要做的就是组织一些安全性的问题。
部份代码:upload instance
复制代码 代码如下:
using system;
using system.io;
using microsoft.web.services2;
using microsoft.web.services2.dime;
namespace webb.wave.winupload
{
/**//// <summary>
/// summary description for controls.
/// </summary>
public class uploadinstance2
{
fields#region fields
private string m_guid;
private datetime m_uploadtime;
private long m_filelength;
private long m_currentpoint;
private string m_pathonserver;
private long m_userid;
#endregion
properties#region properties
public long userid
{
get{return this.m_userid;}
set{this.m_userid=value;}
}
public string guid
{
get{return this.m_guid;}
set{this.m_guid=value;}
}
public datetime uploadtime
{
get{return this.m_uploadtime;}
set{}
}
public long filelength
{
get{return this.m_filelength;}
set{this.m_filelength=value;}
}
public long currentpoing
{
get{return this.m_currentpoint;}
set{this.m_currentpoint=value;}
}
public string pathonserver
{
get{return this.m_pathonserver;}
set{this.m_pathonserver=value;}
}
public string fullpathonserver
{
get
{
if(this.m_guid!=string.empty&&this.m_pathonserver!=string.empty)
{
return path.combine(this.m_pathonserver,this.m_guid+".rem");
}
else
{
return string.empty;
}
}
}
public string filename
{
get
{
if(this.m_guid!=string.empty)
{
return this.m_guid+".rem";
}
else
{
return string.empty;
}
}
}
#endregion
public uploadinstance2()
{
this.m_guid = system.guid.newguid().tostring();
this.m_uploadtime = system.datetime.now;
this.m_currentpoint = 0;
this.m_filelength = 0;
this.m_pathonserver = string.empty;
}
public uploadinstance2(string i_path,string i_guid,long i_filelength)
{
string m_fullpath = path.combine(i_path,i_guid);
if(!file.exists(m_fullpath)) return;
this.m_guid = i_guid;
this.m_uploadtime = system.datetime.now;
this.m_pathonserver = i_path;
fileinfo m_fileinfo = new fileinfo(m_fullpath);
this.m_currentpoint = m_fileinfo.length;
this.m_filelength = i_filelength;
}
public bool uploaddata(byte[] i_data, long i_currentpoint, int i_datasize)
{
string m_fullpath = this.fullpathonserver;
if(!file.exists(m_fullpath)&&this.m_currentpoint!=0)return false;
long m_filepoint = new fileinfo(m_fullpath).length;
if(m_filepoint!=i_currentpoint) return false;
filestream m_filestream = new filestream(m_fullpath,filemode.append);
m_filestream.write(i_data,0,i_datasize);
m_filestream.close();
return true;
}
public void abandantupload()
{
string m_fullpath = this.fullpathonserver;
try{file.delete(m_fullpath);}
catch{}
}
public void createfile()
{
string m_fullpath = this.fullpathonserver;
if(!file.exists(m_fullpath))
{
file.create(m_fullpath).close();
}
else
{
try
{
file.delete(m_fullpath);
}catch{}
file.create(m_fullpath).close();
}
}
}
}
上传过程:
复制代码 代码如下:
#region uploadprocess
public void uploadprocess()
{
datetime m_start = datetime.now;
this.textbox_outmsg.appendtext("initialize upload\r\n");
if(this.m_upload==null||this.m_uploadguid==null||this.m_uploadguid==string.empty)
{
this.textbox_outmsg.appendtext("upload instance id error or login to the server faild\r\n");
this.textbox_outmsg.appendtext("upload faild.\r\n");
return;
}
this.textbox_outmsg.appendtext("open file\r\n");
if(this.m_filepath==null||this.m_filepath==string.empty||!file.exists(this.m_filepath))
{
this.textbox_outmsg.appendtext("open file error\r\n");
this.textbox_outmsg.appendtext("upload faild.\r\n");
return;
}
fileinfo m_fileinfo = new fileinfo(this.m_filepath);
filestream m_fs = new filestream(this.m_filepath, filemode.open, fileaccess.read);
this.textbox_outmsg.appendtext("start upload file\r\n");
int m_buffer = 10; //kbytes
long m_currentpoint = 0;
long m_filelength = m_fileinfo.length;
bool m_uploadresult = false;
byte[] m_data = new byte[m_buffer*1024];
long m_readbytes = m_fs.read(m_data, 0, m_buffer*1024);
this.uploadprocessbar.maximum = 100;
this.uploadprocessbar.minimum = 0;
while(m_readbytes>0)
{
memorystream m_memorystream = new memorystream(m_data, 0,(int)m_readbytes);
dimeattachment dimeattach = new dimeattachment("image/gif", typeformat.mediatype, m_memorystream);
this.m_upload.requestsoapcontext.attachments.add(dimeattach);
m_uploadresult = this.m_upload.uploadfiledata(this.m_uploadinstance,m_currentpoint,m_readbytes);
if(m_uploadresult)
{
m_currentpoint +=m_readbytes;
m_readbytes = m_fs.read(m_data,0,m_buffer*1024);
// this.textbox_outmsg.appendtext("uploading:"+m_currentpoint.tostring()+"/"+m_filelength.tostring()+"\r\n");
this.uploadprocessbar.value = (int)(m_currentpoint*100/m_filelength);
this.label_outpercent.text = this.uploadprocessbar.value.tostring()+"%";
}
else
{
this.textbox_outmsg.appendtext("upload file error.\r\n");
m_fs.close();
this.m_upload.abandantupload(this.m_uploadinstance);
return;
}
}
this.textbox_outmsg.appendtext("file upload finished.\r\n");
this.button_cancel.enabled = false;
m_fs.close();
this.resetform();
}
#endregion
测试项目代码:
http://test.0579fw.com/myfile/kiyeer/客户上传/webbwinupload.zip
出现错误的解决方法:
*****************************************************
引用内容
error 1 'winformtest.localhost.webbwinupload' does not contain a definition for 'requestsoapcontext' d:\webbwinupload\winformtest\webbwinupload.cs 448 19 winformtest
当你更新web引用的时候,.net自动生成的web引用为:
public class webbwinupload : system.web.services.protocols.soaphttpclientprotocol
请转化为:
public class webbwinupload : microsoft.web.services2.webservicesclientprotocol
查找引用下自动生成的c#文件reference.cs