当前位置:首页 > 技术 > 正文内容

基于 WCF 实现 Silverlight 文件分片上传与进度追踪

访客 技术 2026年9月7日 2

在 Silverlight 应用中处理文件上传时,直接使用普通的 HTTP 请求可能会受到诸多限制。通过 WCF (Windows Communication Foundation) 服务来实现文件分片上传,不仅可以突破单次传输的大小限制,还能轻松实现上传进度的精确追踪。本文将详细讲解如何利用 WCF 结合流式分块读取技术,在 Silverlight 中实现高效、低内存占用的文件上传功能。

服务端 WCF 接口实现

首先,在 ASP.NET 宿主项目中添加一个"启用 Silverlight 的 WCF 服务"。为了避免大文件传输导致的内存溢出,服务端接口设计为接收文件分块(Chunk),并根据参数决定是创建新文件还是追加到现有文件。

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class FileTransferService
{
    [OperationContract]
    public void UploadChunk(string targetFileName, byte[] chunkData, bool isAppendMode)
    {
        // 定义上传目录,此处使用 App_Data 避免直接暴露在 Web 根目录
        string uploadDirectory = System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/Uploads");
        
        if (!System.IO.Directory.Exists(uploadDirectory))
        {
            System.IO.Directory.CreateDirectory(uploadDirectory);
        }

        string fullPath = System.IO.Path.Combine(uploadDirectory, targetFileName);
        System.IO.FileMode fileMode = isAppendMode ? System.IO.FileMode.Append : System.IO.FileMode.Create;

        // 将接收到的数据块写入文件
        using (System.IO.FileStream fs = new System.IO.FileStream(fullPath, fileMode, System.IO.FileAccess.Write))
        {
            fs.Write(chunkData, 0, chunkData.Length);
        }
    }
}

客户端服务引用配置

在 Silverlight 项目中,右键点击项目节点并选择"添加服务引用"。在弹出的对话框中点击"发现"按钮,Visual Studio 会自动查找当前解决方案中的 WCF 服务。选中刚才创建的 FileTransferService,命名空间可保持默认或自定义,点击确定完成引用。注意:如果在发现服务时出现错误,请确保先按 F5 编译并运行一次宿主 Web 项目。

客户端 UI 设计

为了直观展示上传状态,我们在 XAML 中放置一个触发按钮和一个用于显示进度信息的文本块。

<UserControl x:Class="SilverlightUploader.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel Orientation="Vertical" Margin="20">
        <Button x:Name="BtnSelectAndUpload" Content="选择并上传文件" Width="160" Height="40" Click="BtnSelectAndUpload_Click"/>
        <TextBlock x:Name="TxtProgress" Margin="0,15,0,0" Text="准备就绪" FontSize="14"/>
    </StackPanel>
</UserControl>

客户端业务逻辑与流式分块读取

原生的实现方式往往会将整个文件读取到内存中的集合里,这在处理大文件时极易引发 OutOfMemoryException。以下重构后的逻辑采用 Stream 游标机制,每次仅从磁盘读取固定大小的字节块(如 4KB)进行异步发送,大幅降低了客户端的内存压力。

public partial class MainPage : UserControl
{
    private const int ChunkSize = 4096; // 每次上传 4KB
    private FileUploadState _uploadState;

    public MainPage()
    {
        InitializeComponent();
    }

    private void BtnSelectAndUpload_Click(object sender, RoutedEventArgs e)
    {
        OpenFileDialog dialog = new OpenFileDialog
        {
            Filter = "所有文件 (*.*)|*.*",
            Multiselect = false
        };

        if (dialog.ShowDialog() == true)
        {
            FileInfo selectedFile = dialog.File;
            _uploadState = new FileUploadState
            {
                FileName = selectedFile.Name,
                TotalBytes = selectedFile.Length,
                UploadedBytes = 0,
                FileStream = selectedFile.OpenRead()
            };

            // 开始上传第一个数据块
            SendNextChunk(false);
        }
    }

    private void SendNextChunk(bool isAppend)
    {
        byte[] buffer = new byte[ChunkSize];
        int bytesRead = _uploadState.FileStream.Read(buffer, 0, ChunkSize);
        _uploadState.LastChunkSize = bytesRead;

        // 如果读取字节数为 0,说明文件已传输完毕
        if (bytesRead == 0)
        {
            _uploadState.FileStream.Close();
            TxtProgress.Text = "上传完成!";
            BtnSelectAndUpload.IsEnabled = true;
            return;
        }

        // 截取实际读取到的字节数组
        byte[] chunkToSend = new byte[bytesRead];
        Array.Copy(buffer, chunkToSend, bytesRead);

        var client = new FileTransferServiceClient();
        client.UploadChunkCompleted += Client_UploadChunkCompleted;
        
        BtnSelectAndUpload.IsEnabled = false;
        client.UploadChunkAsync(_uploadState.FileName, chunkToSend, isAppend, _uploadState);
    }

    private void Client_UploadChunkCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
    {
        if (e.Error != null)
        {
            TxtProgress.Text = "上传出错: " + e.Error.Message;
            _uploadState.FileStream.Close();
            BtnSelectAndUpload.IsEnabled = true;
            return;
        }

        _uploadState = e.UserState as FileUploadState;
        _uploadState.UploadedBytes += _uploadState.LastChunkSize;

        // 计算并更新进度显示
        double progress = (_uploadState.UploadedBytes * 100.0) / _uploadState.TotalBytes;
        TxtProgress.Text = $"进度: {progress:F2}% ({_uploadState.UploadedBytes} / {_uploadState.TotalBytes} 字节)";

        // 继续递归上传下一个数据块
        SendNextChunk(true);
    }
}

/// <summary>
/// 维护单次上传任务的状态上下文
/// </summary>
public class FileUploadState
{
    public string FileName { get; set; }
    public long TotalBytes { get; set; }
    public long UploadedBytes { get; set; }
    public System.IO.Stream FileStream { get; set; }
    public int LastChunkSize { get; set; }
}
标签: SilverlightWCF

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。