使用 WCF 和 Silverlight 进行分块下载






4.81/5 (13投票s)
使用 WCF 服务和 Silverlight 分块下载大文件。
致谢
该项目是关于以分块方式下载大文件 (GB)。您可以在更短的时间内下载大文件,而不会出现任何错误。大多数人在视频流和 P2P 网络中使用分块。在 P2P 网络中,分块选择至关重要。分块的编写目的是为了实现一个目的,并做好它:将文件分割成不同大小的块,主要是为了可以通过小于文件大小的介质传输大文件。
引言
我们需要一个可以下载大文件的功能。一次性下载大文件是有风险的,因此想法是从 UI(Silverlight 应用程序)中,用户可以调用 WCF 函数来下载分块(比如 10 MB) 每次调用。一旦 WCF 返回分块数据,Silverlight 应用程序将打开 FileDialog
并将数据写入流,如果文件大小大于分块大小,程序将再次调用 WCF 函数并获取下一个分块。
通过这种方式,您可以安全地下载大文件。在这里,我们将使用 Silverlight 和 WCF 技术。
背景
假设您想从 UI(在客户端运行)下载大文件,并且该文件位于服务器端。您可以调用 WCF 函数并获取数据块,然后将这些数据写入 FileStream
。WCF 函数将是递归的,因此此函数将获取数据块,直到整个文件下载完毕。
Using the Code
附加的解决方案包含两个项目,一个是 Silverlight,另一个是 Web 项目,其中包含 XAP 文件以及 Service 类。
以下函数位于 Silverlight 页面中,该页面调用 WCF 函数以获取分块数据
注意:您必须在代码中输入有效的 fileSize
(文档的大小,以字节为单位)。
public void DownloadFile(string DocURL)
{
// first you should get the file size from the server side in bytes.
fileSize = 825344;
service = new DownloadFileClient();
isFirstCall = true;
service.InnerChannel.OperationTimeout = new TimeSpan(0, 30, 0);
docName = DocURL;
service.DownloadChunkAsync(DocURL, I64Offset, i32ChunkSize);
service.DownloadChunkCompleted +=
new EventHandler<DownloadChunkCompletedEventArgs>(
service_DownloadChunkCompleted);
}
以下行将 WCF 调用时间增加到 30 分钟,在某些情况下,当您的 WCF 函数需要更长的时间来执行某些操作时,它会发生 TomeOut
。为了防止这种情况,请编写以下代码。
service.InnerChannel.OperationTimeout = new TimeSpan(0, 30, 0);
以下函数是下载完成
void service_DownloadChunkCompleted(object sender, DownloadChunkCompletedEventArgs e)
{
try
{
Byte[] byteArrayFile = (Byte[])e.Result;
if (isFirstCall)
{
MessageBox _oBox = MessageBox.Show("Download file in chunk",
"Are you sure to download the file ?", MsgBoxButtons.YesNo,
MsgBoxIcon.Error, OnDialogClosed);
}
isFirstCall = false;
if (fileDialog != null)
{
WriteIntoFile(fileDialog, byteArrayFile);
I64Offset = I64Offset + i32ChunkSize;
if (I64Offset < fileSize)
{
IsFileToAppend = true;
service.DownloadChunkAsync(docName, I64Offset, i32ChunkSize);
}
else
{
I64Offset = 0;
fileStream.Close();
System.Windows.MessageBox.Show("File downloaded successfully.");
}
}
else
service.DownloadChunkAsync(docName, I64Offset, i32ChunkSize);
}
catch (Exception ex)
{
}
}
对于 MessageBox
,请参阅 MessageBox.xaml 文件。在 Silverlight 中,要打开 fileDialog
,您需要从用户发起的事件中调用它。我创建了一个自定义的 MessageBox
类,它是基于 Model 的。
WriteIntoFile
函数会将数据写入 Stream
。您可以在上面的代码中看到,我一直在调用 WCF 函数,直到它满足 if (I64Offset < fileSize) 条件。
下载完成后,我关闭 FileStream
,并向用户显示成功消息。
WCF 设置
在 web.config 中,进行以下设置
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior
name="DownloadFileInChunk.Web.Service.DownloadFileBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="myServicesBinding"
receiveTimeout="00:10:00" sendTimeout="00:10:00"
openTimeout="00:10:00" closeTimeout="00:10:00"
maxReceivedMessageSize="2147483647"
maxBufferSize="2147483647" maxBufferPoolSize="2147483647">
<readerQuotas maxDepth="256" maxStringContentLength="2147483647"
maxArrayLength="2147483647" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
</binding>
</basicHttpBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<services>
<service
behaviorConfiguration="DownloadFileInChunk.Web.Service.DownloadFileBehavior"
name="DownloadFileInChunk.Web.Service.DownloadFile">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="myServicesBinding"
contract="DownloadFileInChunk.Web.Service.IDownloadFile"/>
</service>
</services>
</system.serviceModel>
下面显示了分块下载的 WCF 函数
public byte[] DownloadChunk(String DocUrl, Int64 Offset, Int32 BufferSize)
{
String FilePath = HttpContext.Current.Server.MapPath(DocUrl);
if (!System.IO.File.Exists(FilePath))
return null;
Int64 FileSize = new FileInfo(FilePath).Length;
//// if the requested Offset is larger than the file, quit.
if (Offset > FileSize)
{
return null;
}
// open the file to return the requested chunk as a byte[]
byte[] TmpBuffer;
int BytesRead;
try
{
using (FileStream fs = new FileStream(FilePath, FileMode.Open,
FileAccess.Read, FileShare.Read))
{
fs.Seek(Offset, SeekOrigin.Begin);
// this is relevant during a retry. otherwise, it just seeks to
// the start
TmpBuffer = new byte[BufferSize];
// read the first chunk in the buffer (which is re-used for
// every chunk)
BytesRead = fs.Read(TmpBuffer, 0, BufferSize);
}
if (BytesRead != BufferSize)
{
// the last chunk will almost certainly not fill the buffer,
// so it must be trimmed before returning
byte[] TrimmedBuffer = new byte[BytesRead];
Array.Copy(TmpBuffer, TrimmedBuffer, BytesRead);
return TrimmedBuffer;
}
else
return TmpBuffer;
}
catch (Exception ex)
{
return null;
}
}
WCF 接口
[ServiceContract]
interface IDownloadFile
{
[OperationContract]
byte[] DownloadChunk(String Docurl, Int64 Offset, Int32 BufferSize);
}
关注点
需要下载大文件的人可以使用此方法。分块下载速度更快、更可靠。我们可以在视频流中使用此方法,您必须从服务器获取数据并将视频数据加载到客户端。还有一个有趣的点是在 P2P 网络中使用分块下载。在这种情况下,P2P 网络中的分块选择算法非常方便。数据驱动的 P2P 流系统可能会为大量观看者提供良好的播放速率。此类 P2P 系统中一个重要的设计问题是确定最佳分块选择策略,该策略在服务器的上传容量约束下提供高连续性播放。
此外,我们可以使用 MTOM Web 服务从服务器获取分块。
参考文献
- https://codeproject.org.cn/KB/XML/MTOMWebServices.aspx
- 基于自适应队列的 P2P 直播分块调度
- http://www.cse.cuhk.edu.hk/~cslui/PUBLICATION/technical-report-P2P.pdf
历史
这是使用 Silverlight 项目中的 WCF 进行分块下载的第一个版本。