C# 以文件流的形式返回本地文件或远程文件
FileStream和FileInfo只能处理本地文件路径,无法直接处理HTTP URL。以下是几种实现远程PDF返回给前端的解决方案:
方案1:使用HttpClient下载远程文件(推荐)
[HttpGet]
public async Task<HttpResponseMessage> GetReportFile()
{
try
{
string orderNo = HttpContext.Current.Request.QueryString["orderNo"];
string filePath = "D:\\1.pdf";
if(!string.IsNullOrEmpty(orderNo))
{
filePath = "http://www.a.com/1.pdf";
// 使用HttpClient下载远程文件
using (var httpClient = new HttpClient())
{
// 设置超时时间
httpClient.Timeout = TimeSpan.FromSeconds(30);
// 下载文件内容
var response = await httpClient.GetAsync(filePath);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsByteArrayAsync();
// 创建返回结果
var result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new ByteArrayContent(content);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("inline")
{
FileName = "document.pdf"
};
return result;
}
}
else
{
// 处理本地文件
FileInfo foundFileInfo = new FileInfo(filePath);
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
var result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StreamContent(fs);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("inline")
{
FileName = foundFileInfo.Name
};
return result;
}
}
catch (Exception ex)
{
logger.Error(ex, ex.Message);
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
}
方案2:流式传输(节省内存)【推荐】
[HttpGet]
public async Task<HttpResponseMessage> GetReportFile()
{
try
{
string orderNo = HttpContext.Current.Request.QueryString["orderNo"];
string filePath = "D:\\1.pdf";
if(!string.IsNullOrEmpty(orderNo))
{
filePath = "http://www.a.com/1.pdf";
using (var httpClient = new HttpClient())
{
httpClient.Timeout = TimeSpan.FromSeconds(30);
// 流式下载
var response = await httpClient.GetAsync(filePath, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
var stream = await response.Content.ReadAsStreamAsync();
var result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("inline")
{
FileName = "document.pdf"
};
return result;
}
}
else
{
// 本地文件处理...
}
}
catch (Exception ex)
{
logger.Error(ex, ex.Message);
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
}
方案3:添加缓存和错误处理
[HttpGet]
public async Task<HttpResponseMessage> GetReportFile()
{
try
{
string orderNo = HttpContext.Current.Request.QueryString["orderNo"];
string filePath = "D:\\1.pdf";
if(!string.IsNullOrEmpty(orderNo))
{
filePath = "http://www.a.com/1.pdf";
using (var httpClient = new HttpClient())
{
// 添加User-Agent头,有些服务器需要
httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0");
httpClient.Timeout = TimeSpan.FromSeconds(30);
// 先获取头部信息检查文件是否存在
var headResponse = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, filePath));
if (!headResponse.IsSuccessStatusCode)
{
return new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent("远程文件未找到")
};
}
// 获取文件名(从Content-Disposition或URL中提取)
string fileName = "document.pdf";
if (headResponse.Content.Headers.ContentDisposition != null)
{
fileName = headResponse.Content.Headers.ContentDisposition.FileName ?? fileName;
}
// 下载文件
var getResponse = await httpClient.GetAsync(filePath, HttpCompletionOption.ResponseHeadersRead);
getResponse.EnsureSuccessStatusCode();
var result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StreamContent(await getResponse.Content.ReadAsStreamAsync());
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("inline")
{
FileName = fileName
};
// 添加缓存头(可选)
result.Headers.CacheControl = new CacheControlHeaderValue()
{
MaxAge = TimeSpan.FromHours(1)
};
return result;
}
}
else
{
// 本地文件处理...
}
}
catch (HttpRequestException httpEx)
{
logger.Error(httpEx, "网络请求错误");
return new HttpResponseMessage(HttpStatusCode.BadGateway);
}
catch (TaskCanceledException timeoutEx)
{
logger.Error(timeoutEx, "请求超时");
return new HttpResponseMessage(HttpStatusCode.RequestTimeout);
}
catch (Exception ex)
{
logger.Error(ex, ex.Message);
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
}
重要注意事项:
- 异步方法:将方法改为
async Task<HttpResponseMessage>以支持异步操作 - 资源释放:确保正确释放
HttpClient和流资源 - 超时处理:为远程请求设置合理的超时时间
- 错误处理:添加针对网络请求的特定错误处理
- 内存考虑:对于大文件,使用流式传输避免内存溢出
推荐使用方案2的流式传输,因为它内存效率更高,特别适合处理大文件。
本文来自博客园,作者:VipSoft 转载请注明原文链接:https://www.cnblogs.com/vipsoft/p/19049903
浙公网安备 33010602011771号