You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过AJAX调用ASP.NET Core控制器获取Zip文件时文件无效问题

问题:ASP.NET Core控制器生成的Zip文件通过AJAX返回后无法正常解压

我在Stack Overflow及网络上搜索多日,试过各种ASP.NET Core控制器生成Zip文件并通过AJAX返回给客户端的示例,但下载的Zip文件始终无效。

AJAX请求会提交Zip文件需包含的内容数据,服务器通过HTTP调用从内容管理系统获取图片并压缩为Zip文件。我在JS里把返回的数据转成Blob,再用window.URL.createObjectURL处理后设置到弹窗的锚点供用户下载。已经验证过服务器端生成的Zip数据写入磁盘后是有效的,问题出在前端处理环节。

前端AJAX代码

$.ajax({
    url: downloadUrl,
    type: 'post',
    data: "searchText=".concat(modelData).concat("&source=").concat(target.id),
}).done(function (data, status, xhr) {
    var contentType = xhr.getResponseHeader("Content-Type");

    var blob = new Blob([data], {
        type: contentType
    });

    // 设置Blob到下载链接
    var aDownload = document.getElementById('download-file');
    aDownload.href = window.URL.createObjectURL(blob);
    aDownload.download = "downloadfile.zip"

    // 弹出下载窗口
    document.getElementById("embed-popup").hidden = true;
    const embedCode = document.getElementById('embed-popup');
    embedCode.value = target.dataset.embed;
    document.body.classList.add('show-embed');
    requestAnimationFrame(() => {
        autosize.update(embedCode);
    });
});

后端控制器代码

[HttpPost]
public async Task<IActionResult> ResourcesDownload(ResourceSearch resourceSearch)
{
    ResourceSearchResult resourceResult = JsonConvert.DeserializeObject<ResourceSearchResult>(resourceSearch.SearchText);

    string[] resourceSearchSplits = resourceSearch.Source.Split('|');
    SubCategory subCategory = resourceResult.SubCategories.SingleOrDefault(x => x.Name == resourceSearchSplits.Last());

    using (MemoryStream zipStream = new MemoryStream())
    {
        using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
        {
            foreach (Resource resource in subCategory.Resources)
            {
                string fileSuffix = resource.FileType.Split('/')[1];

                string title = Regex.Replace(resource.Title, "[^a-zA-Z0-9]", String.Empty);
                ZipArchiveEntry entry = zip.CreateEntry($"{title}.{fileSuffix}");

                using (HttpClient httpClient = new HttpClient())
                {
                    using (Stream stream = await httpClient.GetStreamAsync(new Uri(resource.FileUrl)))
                    {
                        using (Stream entryStream = entry.Open())
                        {
                            await stream.CopyToAsync(entryStream);
                        }
                    }
                }
            }
        }

        zipStream.Position = 0;

        return File(zipStream.ToArray(), "application/zip", "downloadfile.zip"); 
    }
}

操作流程

  • 点击链接触发AJAX请求,提交Zip应包含的资源信息:
    触发AJAX请求的链接
  • 服务器返回Zip数据后弹出窗口,用户点击链接即可下载文件:
    下载弹窗

解决方案

问题核心是AJAX请求未指定responseType,默认情况下jQuery会把二进制Zip数据解析为字符串,导致Blob生成错误,最终文件损坏。

1. 修改AJAX请求,指定响应类型为Blob

$.ajax({
    url: downloadUrl,
    type: 'post',
    data: "searchText=".concat(modelData).concat("&source=").concat(target.id),
    xhrFields: {
        responseType: 'blob' // 关键:告诉浏览器直接处理二进制Blob数据
    }
}).done(function (blob, status, xhr) {
    // 直接使用返回的Blob对象,无需二次转换
    var aDownload = document.getElementById('download-file');
    aDownload.href = window.URL.createObjectURL(blob);
    aDownload.download = "downloadfile.zip"

    // 弹窗逻辑保留
    document.getElementById("embed-popup").hidden = true;
    const embedCode = document.getElementById('embed-popup');
    embedCode.value = target.dataset.embed;
    document.body.classList.add('show-embed');
    requestAnimationFrame(() => {
        autosize.update(embedCode);
    });
});

2. 后端代码优化建议

  • 移除未使用的HttpResponseMessage代码块,直接返回File结果即可
  • 复用HttpClient(不要在循环内重复创建),通过依赖注入IHttpClientFactory提升性能
  • 增加空值判断,避免空引用异常

优化后的后端代码示例:

private readonly IHttpClientFactory _httpClientFactory;

// 构造函数注入IHttpClientFactory
public YourControllerName(IHttpClientFactory httpClientFactory)
{
    _httpClientFactory = httpClientFactory;
}

[HttpPost]
public async Task<IActionResult> ResourcesDownload(ResourceSearch resourceSearch)
{
    if (string.IsNullOrEmpty(resourceSearch.SearchText) || string.IsNullOrEmpty(resourceSearch.Source))
    {
        return BadRequest("无效的请求参数");
    }

    ResourceSearchResult resourceResult = JsonConvert.DeserializeObject<ResourceSearchResult>(resourceSearch.SearchText);
    string[] resourceSearchSplits = resourceSearch.Source.Split('|');
    if (resourceSearchSplits.Length == 0)
    {
        return BadRequest("Source参数格式错误");
    }

    SubCategory subCategory = resourceResult?.SubCategories?.SingleOrDefault(x => x.Name == resourceSearchSplits.Last());
    if (subCategory == null || !subCategory.Resources.Any())
    {
        return NotFound("未找到可下载的资源");
    }

    using (MemoryStream zipStream = new MemoryStream())
    {
        using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
        {
            var httpClient = _httpClientFactory.CreateClient();
            foreach (Resource resource in subCategory.Resources)
            {
                if (string.IsNullOrEmpty(resource.FileUrl) || string.IsNullOrEmpty(resource.FileType))
                {
                    continue;
                }

                string[] fileTypeParts = resource.FileType.Split('/');
                if (fileTypeParts.Length < 2)
                {
                    continue;
                }
                string fileSuffix = fileTypeParts[1];

                string title = Regex.Replace(resource.Title, "[^a-zA-Z0-9]", String.Empty);
                ZipArchiveEntry entry = zip.CreateEntry($"{title}.{fileSuffix}");

                using (Stream stream = await httpClient.GetStreamAsync(new Uri(resource.FileUrl)))
                using (Stream entryStream = entry.Open())
                {
                    await stream.CopyToAsync(entryStream);
                }
            }
        }

        zipStream.Position = 0;
        return File(zipStream.ToArray(), "application/zip", "downloadfile.zip"); 
    }
}

问题原因说明

默认情况下jQuery的AJAX会把响应数据当作UTF-8字符串解析,二进制Zip文件包含大量非UTF-8字符,解析过程中会丢失关键数据,后续转成Blob时已经是损坏的文件。指定responseType: 'blob'后,浏览器会直接保留原始二进制数据,生成的Blob才能正确还原为有效Zip文件。


内容的提问来源于stack exchange,提问作者Eric Wilson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 07:14:51