65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 WCF 和 Silverlight 进行分块下载

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (13投票s)

2010 年 9 月 7 日

CPOL

3分钟阅读

viewsIcon

80382

downloadIcon

3067

使用 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 服务从服务器获取分块。

参考文献

历史

这是使用 Silverlight 项目中的 WCF 进行分块下载的第一个版本。

© . All rights reserved.