浏览器点击按钮无法下载CSV文件问题求助
问题:点击按钮无法下载CSV文件
点击浏览器按钮后无任何反应,但已确认本地临时文件夹中CSV文件已生成。以下是相关代码:
API 代码(C#)
[HttpGet] [Route("[controller]/downlaodCSV/{pID}/{customerID}")] public System.Net.Http.HttpResponseMessage downlaodCSV(int pID, int customerID) { //获取文件 var rateFilename = _Client.RateRepository.downlaodCSV(pID, customerID); var stream = new System.IO.MemoryStream(); //处理流(原代码此处省略) var result = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.OK) { Content = new System.Net.Http.ByteArrayContent(stream.ToArray()) }; result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment") { FileName = rateFilename }; result.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream"); return result; }
业务逻辑层(Rates Repository)
public string downlaodCSV(int pID, int customerID) { Random _random = new Random(); int number = _random.Next(1, 9999); string fileName = ""; try { #region 测试数据 //临时测试数据,后续将替换为API获取的真实数据 DataTable rates = new DataTable(); rates.Clear(); rates.Columns.Add("Name"); rates.Columns.Add("Surname"); DataRow dr = rates.NewRow(); dr["Name"] = "Test1"; dr["Surname"] = "Test2"; rates.Rows.Add(dr); #endregion if (rates.Rows.Count > 0) { StringBuilder stringBuilder = new StringBuilder(); string[] columnNames = rates.Columns.Cast<DataColumn>().Select(column => column.ColumnName).ToArray(); stringBuilder.AppendLine(string.Join(",", columnNames)); foreach (DataRow row in rates.Rows) { string[] fields = row.ItemArray.Select(field => field.ToString()).ToArray(); stringBuilder.AppendLine(string.Join(",", fields)); } fileName = CsvFilename(number); //保存文件用于验证(后续会删除) string html = String.Empty; var newName = fileName; var path = @"C:\test\ " + newName + ""; File.WriteAllText(fileName, stringBuilder.ToString()); File.SetAttributes(fileName, FileAttributes.Normal); File.WriteAllText(path, html); } else { throw new Exception("未找到可下载的费率数据!"); } } catch (Exception ex) { throw ex; } return fileName; }
Angular 组件代码(Component.ts)
downloadFile() { try { var _pID = this.reseller.pID; var _customerID = this.reseller.customerID; //调用下载服务 this.rateSheetService.downlaodCSV(_pID, _customerID).then((obj) => { console.log("API调用成功!"); let blob: Blob = obj as Blob; console.log("blob", blob); var binaryData = []; binaryData.push(blob); console.log("binaryData", binaryData); var url = window.URL.createObjectURL(new Blob(binaryData, {type: "text/csv"})) console.log("Url", url); }); } catch(e) { console.log("错误信息 ", e) } }
Angular 服务代码(Service.ts)
public downlaodCSV(pID, customerID){ return this.apiService.get<any>(`RateSheetReport/downlaodCSV/${pID}/${customerID}`); }
HTML 按钮代码
<button class="context-download-button" (click)="downloadFile()" tooltipPosition="top"> <span class="icon icon-download mr-5"></span>Download </button>
浏览器控制台情况
控制台已打印出Blob对象,但无下载动作触发。
解决方案
问题核心在于前端仅生成Blob URL但未触发下载行为,同时API层存在流处理空数据的错误,以下是分步修复:
1. 修复API层的流处理错误
当前API中的stream未写入任何数据,直接返回空流,需读取生成的CSV文件内容到流中,并修正MIME类型:
[HttpGet] [Route("[controller]/downlaodCSV/{pID}/{customerID}")] public System.Net.Http.HttpResponseMessage downlaodCSV(int pID, int customerID) { var rateFilename = _Client.RateRepository.downlaodCSV(pID, customerID); // 读取本地生成的CSV文件内容到字节数组 byte[] fileBytes = System.IO.File.ReadAllBytes(rateFilename); var stream = new System.IO.MemoryStream(fileBytes); var result = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.OK) { Content = new System.Net.Http.ByteArrayContent(stream.ToArray()) }; result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment") { FileName = rateFilename }; // 改为CSV对应的MIME类型 result.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/csv"); return result; }
2. 前端添加下载触发逻辑
生成Blob URL后,通过创建临时<a>标签模拟点击,触发浏览器下载:
downloadFile() { try { var _pID = this.reseller.pID; var _customerID = this.reseller.customerID; this.rateSheetService.downlaodCSV(_pID, _customerID).then((obj) => { console.log("API调用成功!"); let blob: Blob = obj as Blob; const url = window.URL.createObjectURL(blob); // 创建临时a标签 const downloadLink = document.createElement('a'); downloadLink.href = url; // 指定下载文件名 downloadLink.download = `rates_${_pID}_${_customerID}.csv`; document.body.appendChild(downloadLink); // 模拟点击触发下载 downloadLink.click(); // 清理资源 window.URL.revokeObjectURL(url); document.body.removeChild(downloadLink); }); } catch(e) { console.log("错误信息 ", e) } }
3. 确保API请求返回Blob类型
修改Angular服务,指定响应类型为blob,避免二进制数据被错误解析为JSON:
public downlaodCSV(pID, customerID){ return this.apiService.get<any>(`RateSheetReport/downlaodCSV/${pID}/${customerID}`, { responseType: 'blob' }); }
4. 清理业务逻辑冗余代码
业务逻辑中File.WriteAllText(path, html);是写入空内容的冗余操作,建议删除;同时确保CsvFilename(number)方法返回带.csv后缀的正确文件名。
内容的提问来源于stack exchange,提问作者Ribon
相关产品推荐
相关产品推荐

