我正在尝试使用Blazor输入文件,以及imagessharp库,将IBrowserFile转换为图像。
我的方法是这样的
public async Task<byte[]> ConvertFileToByteArrayAsync(IBrowserFile file)
{
using var image = Image.Load(file.OpenReadStream());
image.Mutate(x => x.Resize(new ResizeOptions
{
Mode = ResizeMode.Min,
Size = new Size(128)
}));
MemoryStream memoryStream = new MemoryStream();
if (file.ContentType == "image/png")
{
await image.SaveAsPngAsync(memoryStream);
}
else
{
await image.SaveAsJpegAsync(memoryStream);
}
var byteFile = memoryStream.ToArray();
memoryStream.Close();
memoryStream.Dispose();
return byteFile;
}
但是我得到以下错误:
crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: Synchronous reads are not supported.
System.NotSupportedException: Synchronous reads are not supported.
at Microsoft.AspNetCore.Components.Forms.BrowserFileStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.Stream.CopyTo(Stream destination, Int32 bufferSize)
at SixLabors.ImageSharp.Image.WithSeekableStream[ValueTuple`2](Configuration configuration, Stream stream, Func`2 action)
at SixLabors.ImageSharp.Image.Load(Configuration configuration, Stream stream, IImageFormat& format)
at SixLabors.ImageSharp.Image.Load(Configuration configuration, Stream stream)
at SixLabors.ImageSharp.Image.Load(Stream stream)
at MasterMealWA.Client.Services.FileService.ConvertFileToByteArrayAsync(IBrowserFile file) in F:CoderFoundryCodeMasterMealWAMasterMealWAClientServicesFileService.cs:line 37
at MasterMealWA.Client.Pages.RecipePages.RecipeCreate.CreateRecipeAsync() in F:CoderFoundryCodeMasterMealWAMasterMealWAClientPagesRecipePagesRecipeCreate.razor:line 128
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.Forms.EditForm.HandleSubmitAsync()
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle)
记录,第37行是"使用var image......",我不太清楚我在哪里使用多个流,除非是读流和内存流。然而,我也不知道如何关闭我用file.OpenReadStream打开的流。
Background:
-
ASP。. NET Core 3+不鼓励开发人员使用非异步IO,因为所有的
Stream
对象都由ASP处理。. NET Core(例如HttpRequest.Body
和IBrowserFile.OpenReadStream()
)在调用非异步的Stream.Read
和Stream.Write
方法时抛出异常。- 查看此线程:https://github.com/dotnet/aspnetcore/issues/7644
- 这个线程:ASP。. NET Core:不允许同步操作。调用WriteAsync或将AllowSynchronousIO设置为true
- 这个行为是由
IHttpBodyControlFeature
控制的-所以你可以重新启用同步流IO如果你绝对有必要,但你真的不应该。
-
同样,您不需要中间的
MemoryStream
:您可以直接将ImageSharp输出写入响应。
妥善解决:
你正在调用ImageSharp的Image.Load
方法,它使用非异步Stream
方法。修复方法是简单地使用await Image.LoadAsync
代替:
所以把你的代码改成:
// I assume this is a Controller Action method
// This method does not return an IActionResult because it writes directly to the response in the action method. See examples here: https://stackoverflow.com/questions/42771409/how-to-stream-with-asp-net-core
public async Task ResizeImageAsync( IBrowserFile file )
{
await using( Stream stream = file.OpenReadStream() )
using( Image image = await Image.LoadAsync( stream ) )
{
ResizeOptions ro = new ResizeOptions
{
Mode = ResizeMode.Min,
Size = new Size(128)
};
image.Mutate( img => img.Resize( ro ) );
if( file.ContentType == "image/png" ) // <-- You should not do this: *never trust* the client to be correct and truthful about uploaded files' types and contents. In this case it's just images so it's not that big a deal, but always verify independently server-side.
{
this.Response.ContentType = "image/png";
await image.SaveAsPngAsync( this.Response.Body );
}
else
{
this.Response.ContentType = "image/jpeg";
await image.SaveAsJpegAsync( this.Response.Body );
}
}
备选(非)解决方案:procrastination:
禁用ASP。. NET Core对非异步IO的禁止:
public void ConfigureServices(IServiceCollection services)
{
// If using Kestrel:
services.Configure<KestrelServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});
// If using IIS:
services.Configure<IISServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});
}
Image.Load
为同步操作。试试async版本:
using var image = await Image.LoadAsync(file.OpenReadStream());
您必须调用异步方法,如LoadAsync
,DisposeAsync()
而不是同步的Dispose()
。使用await using xxx
等待DisposeAsync()
的调用
public async Task<byte[]> ConvertFileToByteArrayAsync(IBrowserFile file)
{
await using var image = await image.LoadAsync(file.OpenReadStream());
image.Mutate(x => x.Resize(new ResizeOptions
{
Mode = ResizeMode.Min,
Size = new Size(128)
}));
MemoryStream memoryStream = new MemoryStream();
if (file.ContentType == "image/png")
{
await image.SaveAsPngAsync(memoryStream);
}
else
{
await image.SaveAsJpegAsync(memoryStream);
}
var byteFile = memoryStream.ToArray();
memoryStream.Close();
await memoryStream.DisposeAsync();
return byteFile;
}