C# 如何将Web Service返回的byte[]格式PDF在新标签页打开
问题根因
- 第一种方案用Ajax请求PDF二进制流时,Ajax默认将响应按文本解析,导致输出乱码,且ASMX服务的JSON序列化配置会干扰二进制流输出。
- 第二种方案直接将返回的
data.d塞入Blob导致文件损坏,是因为ASMX服务会自动将返回的byte[]序列化为Base64字符串,而非原始二进制数据,直接输入Blob会导致编码不匹配。 - 原有Ajax请求的URL中id参数额外包裹了单引号,会导致传入后端的id值带单引号,数据库查询匹配失败。
后端调整(二选一即可)
方案1(推荐):直接输出二进制流,适配浏览器直接访问
无需走Ajax,让浏览器原生处理PDF渲染,后端代码调整如下:
// 去掉Json序列化配置,直接操作Response输出 [WebMethod] [ScriptMethod(UseHttpGet = true)] public void RetrieveSPODData(string id) { string query = @"Select * from dcmnts where BUSNSS_ID = :BUSNSS_ID"; byte[] data = null; string ConnString = ConfigurationManager.ConnectionStrings["DbConnEnc"].ToString(); using (OracleConnection connection = new OracleConnection(BaseEncryptor.DecryptString(ConnString))) { OracleCommand command = null; OracleDataAdapter adapter = null; try { if (connection.State != ConnectionState.Open) connection.Open(); command = new OracleCommand(query, connection); command.Parameters.Add(new OracleParameter("BUSNSS_ID", id.Trim('\''))); // 兼容原有错误的单引号传参,前端去掉单引号后可删除Trim逻辑 adapter = new OracleDataAdapter(command); DataTable dataTable = new DataTable(); adapter.Fill(dataTable); if (dataTable.Rows.Count > 0) { data = dataTable.Rows[0].Field<byte[]>("SUMMARY_SPOD"); } // 空数据处理 if (data == null || data.Length == 0) { HttpContext.Current.Response.StatusCode = 404; HttpContext.Current.Response.End(); return; } HttpContext.Current.Response.ClearHeaders(); HttpContext.Current.Response.AddHeader("Content-Disposition", "inline;filename=file.pdf"); HttpContext.Current.Response.ContentType = "application/pdf"; HttpContext.Current.Response.BinaryWrite(data); HttpContext.Current.Response.Flush(); HttpContext.Current.Response.End(); } finally { adapter?.Dispose(); command?.Dispose(); } } }
同时要在web.config中开启ASMX的GET请求权限:
<system.web> <webServices> <protocols> <add name="HttpGet"/> </protocols> </webServices> </system.web>
方案2:返回Base64编码,适配Ajax请求
后端直接返回byte[],ASMX会自动转为Base64字符串,代码调整如下:
[ScriptMethod(ResponseFormat = ResponseFormat.Json, UseHttpGet = true)] [WebMethod] public byte[] RetrieveSPODData(string id) { string query = @"Select * from dcmnts where BUSNSS_ID = :BUSNSS_ID"; string ConnString = ConfigurationManager.ConnectionStrings["DbConnEnc"].ToString(); using (OracleConnection connection = new OracleConnection(BaseEncryptor.DecryptString(ConnString))) { OracleCommand command = null; OracleDataAdapter adapter = null; try { if (connection.State != ConnectionState.Open) connection.Open(); command = new OracleCommand(query, connection); command.Parameters.Add(new OracleParameter("BUSNSS_ID", id.Trim('\''))); adapter = new OracleDataAdapter(command); DataTable dataTable = new DataTable(); adapter.Fill(dataTable); if (dataTable.Rows.Count > 0) { return dataTable.Rows[0].Field<byte[]>("SUMMARY_SPOD"); } return Array.Empty<byte>(); } finally { adapter?.Dispose(); command?.Dispose(); } } }
前端调整(对应后端方案二选一)
对应后端方案1(最简便,无弹窗拦截问题)
完全不需要Ajax和JS方法,直接修改a标签即可:
<a href="Services/MyService.asmx/RetrieveSPODData?id=<%=TransportationDocument != null ? TransportationDocument.BusinessID.ToString() : String.Empty %>" target="_blank"> <img alt="SPOD" src="images/icons/adobe_acrobat_icon.png"> </a>
对应后端方案2(必须走Ajax的场景)
调整JS代码,将Base64字符串转为二进制后再生成Blob:
function getSPOD(id) { $.ajax({ type: "GET", // 去掉id参数外面的单引号 url: `Services/MyService.asmx/RetrieveSPODData?id=${id}`, contentType: 'application/json; charset=utf-8', success: function (data) { if (!data.d || data.d.length === 0) { alert("未找到对应PDF文件"); return; } // Base64转二进制 const binaryStr = atob(data.d); const byteArray = new Uint8Array(binaryStr.length); for (let i = 0; i < binaryStr.length; i++) { byteArray[i] = binaryStr.charCodeAt(i); } const file = new Blob([byteArray], { type: 'application/pdf' }); const fileURL = URL.createObjectURL(file); window.open(fileURL); }, error: function (textStatus, errorThrown) { console.log(textStatus, errorThrown); } }); }
内容的提问来源于stack exchange,提问作者AngryOtter
相关产品推荐
相关产品推荐

