如何在 ASP.NET Core 中返回存储为 byte[] 的文件?
Posted
技术标签:
【中文标题】如何在 ASP.NET Core 中返回存储为 byte[] 的文件?【英文标题】:How do I return a file stored as byte[] in ASP.NET Core? 【发布时间】:2020-05-16 14:21:17 【问题描述】:我已经搜索了一段时间,虽然它应该很简单,但我无法让它工作。根据我看到的例子,这是我到目前为止的地方:
SomeAppService.cs
public async Task<FileStream> Download(long? id)
var attachment = await _repository.FirstOrDefaultAsync(x => x.Id == id);
var fileStream = new FileStream($"attachment.FileName.attachment.FileExtension",
FileMode.Create, FileAccess.Write);
fileStream.Write(attachment.File, 0, attachment.File.Length);
return fileStream;
可以注意到,“FileName”、“FileExtension”和“File”(即前面提到的字节数组)存储在数据库中。附件可以是任何类型的文件,除了上传方法中禁止的扩展名(未显示)。然后在我的控制器中:
SomeController.cs
[AllowAnonymous]
[HttpGet("Download/id")]
public async Task<IActionResult> Download(long? id)
var fileStream = await appService.Download(id);
return new FileStreamResult(fileStream, "application/octet-stream");
但是,当我点击下载端点时,我最终得到了一个名为“response”的文件,没有扩展名,只有 0 个字节。
资源:
Return file in ASP.Net Core Web API
Return a FileResult from a byte[]
Save and load MemoryStream to/from a file(255 票的回复让我了解了如何将字节数组转换为文件流,但我不知道这是否可行)
【问题讨论】:
您可能需要将流的位置重置为开头。但是,您似乎可以将attachment.File
包装在MemoryStream
中,然后直接将其传递给FileStreamResult
构造函数。还有一个FileContentResult,您可以将attachment.File
传递给而不是创建流。
return
Download()
编辑的 FileStream
return
将立即定位在您刚刚写入的数据之后。 Seek()
ing 在return
ing 之前回到Stream
的开头会产生不同的结果吗?
对于您提供下载的文件,必须明确指定(首选)文件名和长度(编辑:和位置)等内容。我自己不知道这种模式,我只使用过 HTTP 处理程序:c-sharpcorner.com/UploadFile/dacca2/…,甚至在不久前。
@BACON 刚试过。没有骰子。
类似这样,return File(byteArray, "contentType");
内容类型例如是application/pdf
【参考方案1】:
谢谢大家,最后FileContentResult
成功了。
看起来是这样的:
服务
public async Task<Attachment> Download(long? id)
return await _repository.FirstOrDefaultAsync(x => x.Id == id);
控制器
[AllowAnonymous]
[HttpGet("Download/id")]
public async Task<IActionResult> Download(long? id)
var attachment = await appService.Download(id);
return new FileContentResult(attachment.File,
MimeTypeMap.GetMimeType(attachment.FileExtension))
FileDownloadName = $"attachment.NomeFile.attachment.FileExtension"
;
MimeTypeMap
可以找到here
【讨论】:
【参考方案2】:如果您想从实体框架的数据库 blob 流式传输文件而不将其加载到内存中。首先将数据模型拆分为两部分;
public class Attachment
public int Id get; set;
public string Filename get; set;
public string ContentType get; set;
public virtual AttachmentBlob Blob get; set;
//...
public class AttachmentBlob
public int Id get; set;
public byte[] File get; set;
将它们映射到同一个表,但不是作为拥有的类型;
modelBuilder.Entity<Attachment>(e =>
e.HasOne(a => a.Blob)
.WithOne()
.HasForeignKey<AttachmentBlob>(b => b.Id);
);
modelBuilder.Entity<AttachmentBlob>(e =>
e.ToTable("Attachment");
);
然后您可以将它们作为字节数组或流进行读写;
public static async Task Read(DbContext db, Attachment attachment, Func<Stream,Task> callback)
await db.Database.OpenConnectionAsync();
try
var conn = db.Database.GetDbConnection();
var cmd = conn.CreateCommand();
var parm = cmd.CreateParameter();
cmd.Parameters.Add(parm);
parm.ParameterName = "@id";
parm.Value = attachment.Id;
cmd.CommandText = "select File from Attachment where Id = @id";
using (var reader = await cmd.ExecuteReaderAsync())
if (await reader.ReadAsync())
await callback(reader.GetStream(0));
finally
await db.Database.CloseConnectionAsync();
public class AttachmentResult : FileStreamResult
private readonly DbContext db;
private readonly Attachment attachment;
public AttachmentResult(DbContext db, Attachment attachment) : base(new MemoryStream(), attachment.ContentType)
this.db = db;
this.attachment = attachment;
public override async Task ExecuteResultAsync(ActionContext context)
await Read(db, attachment, async s =>
FileStream = s;
await base.ExecuteResultAsync(context);
);
public static async Task Write(DbContext db, Attachment attachment, Stream content)
await db.Database.OpenConnectionAsync();
try
var conn = db.Database.GetDbConnection();
var cmd = conn.CreateCommand();
cmd.Transaction = db.Database.CurrentTransaction?.GetDbTransaction();
var parm = cmd.CreateParameter();
cmd.Parameters.Add(parm);
parm.ParameterName = "@id";
parm.Value = attachment.Id;
parm = cmd.CreateParameter();
cmd.Parameters.Add(parm);
parm.ParameterName = "@content";
parm.Value = content;
cmd.CommandText = "update Attachment set File = @content where Id = @id";
await cmd.ExecuteNonQueryAsync();
finally
await db.Database.CloseConnectionAsync();
public static Task InsertAttachment(DbContext db, Attachment attachment, Stream content)
var strat = db.Database.CreateExecutionStrategy();
return strat.ExecuteAsync(async () =>
using (var trans = await db.Database.BeginTransactionAsync())
db.Set<Attachment>.Add(attachment);
await db.SaveChangesAsync();
await Write(db, attachment, content);
trans.Commit();
db.ChangeTracker.AcceptAllChanges();
);
【讨论】:
这个方法也可以适配reader.GetTextReader()
或者parm.Value = [TextReader]
的大字符串。以上是关于如何在 ASP.NET Core 中返回存储为 byte[] 的文件?的主要内容,如果未能解决你的问题,请参考以下文章
如何在我的ASP.NET Core应用程序中显示来自我的Azure Blob存储帐户的图像?
如何在 ASP.NET Core MVC 中使用依赖注入设计存储库模式?
如何在 ASP.NET Core MVC 中使用 ADO.NET 向存储过程添加参数?