这个问题与:ASP.NET Core Web API upload and download file
首先,我要感谢Powel Gerr,他帮助我理解了他的帖子(http://weblogs.thinktecture.com/pawel/2017/03/aspnet-core-webapi-performance.html)的一些细微差别。我还有一个要解决的问题。
我的情况 假设我们有一个.NET Core控制台应用程序:
private static void Main(string[] args)
{
Console.WriteLine($"Test starts at {DateTime.Now.ToString("o")}");
FileStream fileStream = new FileStream(@"C:\Windows10Upgrade\Windows10UpgraderApp.exe", FileMode.Open);
MyFile vFile = new MyFile()
{
Lenght = 0,
Path = "https://c2calrsbackup.blob.core.windows.net/containername/Windows10UpgraderApp.exe",
RelativePath = "Windows10UpgraderApp.exe"
};
Stream uploadStream = GetUploadStream(vFile).GetAwaiter().GetResult();
fileStream.CopyTo(uploadStream);
Console.WriteLine($"Test ends at {DateTime.Now.ToString("o")}");
Console.Write("Press any key to exit...");
Console.ReadKey();
}
private static async Task<Stream> GetUploadStream(MyFile myFile)
{
Stream stream = null;
try
{
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri("https://localhost:5000");
using (MultipartFormDataContent multipartFormDataContent = new MultipartFormDataContent())
{
multipartFormDataContent.Add(new StringContent(JsonConvert.SerializeObject(myFile), Encoding.UTF8, "application/json"), nameof(MyFile));
HttpResponseMessage httpResult = await httpClient.PostAsync("api/uploaddownload/upload", multipartFormDataContent).ConfigureAwait(false);
httpResult.EnsureSuccessStatusCode();
stream = await httpResult.Content.ReadAsStreamAsync().ConfigureAwait(false);
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return stream;
}
如您所见,MyFile是一个支持类,其中包含一些信息。另一方面,控制器如下:
[HttpPost("upload")]
public async Task<IActionResult> GetUploadStream()
{
const string contentType = "application/octet-stream";
string boundary = GetBoundary(Request.ContentType);
MultipartReader reader = new MultipartReader(boundary, Request.Body, 80 * 1024);
Dictionary<string, string> sectionDictionary = new Dictionary<string, string>();
FileMultipartSection fileMultipartSection;
MultipartSection section;
while ((section = await reader.ReadNextSectionAsync().ConfigureAwait(false)) != null)
{
ContentDispositionHeaderValue contentDispositionHeaderValue = section.GetContentDispositionHeader();
if (contentDispositionHeaderValue.IsFormDisposition())
{
FormMultipartSection formMultipartSection = section.AsFormDataSection();
string value = await formMultipartSection.GetValueAsync().ConfigureAwait(false);
sectionDictionary.Add(formMultipartSection.Name, value);
}
else if (contentDispositionHeaderValue.IsFileDisposition())
{
fileMultipartSection = section.AsFileSection();
}
}
CloudStorageAccount.TryParse(connectionString, out CloudStorageAccount cloudStorageAccount);
CloudBlobClient cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient();
CloudBlobContainer cloudBlobContainer = cloudBlobClient.GetContainerReference(containerName);
if (await cloudBlobContainer.CreateIfNotExistsAsync().ConfigureAwait(false))
{
BlobContainerPermissions blobContainerPermission = new BlobContainerPermissions()
{
PublicAccess = BlobContainerPublicAccessType.Container
};
await cloudBlobContainer.SetPermissionsAsync(blobContainerPermission).ConfigureAwait(false);
}
MyFile myFile = JsonConvert.DeserializeObject<MyFile>(sectionDictionary.GetValueOrDefault(nameof(MyFile)));
CloudBlockBlob cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference(myFile.RelativePath);
Stream streamResult = await cloudBlockBlob.OpenWriteAsync().ConfigureAwait(false);
return new FileStreamResult(streamResult, contentType);
}
当控制器返回指令return new FileStreamResult(streamResult, contentType);
时控制器本身会生成以下异常(不是在调用控制台的应用程序中):
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware:错误:执行请求时发生未处理的异常。
System.NotSupportedException:流不支持读取。 在System.IO.Stream.BeginReadInternal处(字节[]缓冲区,Int32偏移量,Int32计数,AsyncCallback回调,对象状态,布尔SerializeAsynchronously,布尔apm) 在System.IO.Stream.BeginEndReadAsync(Byte []缓冲区,Int32偏移量,Int32计数) 在System.IO.Stream.ReadAsync(Byte []缓冲区,Int32偏移量,Int32计数,CancellationToken cancelledToken) 在Microsoft.AspNetCore.Http.Extensions.StreamCopyOperation.CopyToAsync(流源,流目标,可空1计数,Int32 bufferSize,CancellationToken取消) 在Microsoft.AspNetCore.Mvc.Infrastructure.FileResultExecutorBase.WriteFileAsync(HttpContext上下文,流fileStream,RangeItemHeaderValue范围,Int64 rangeLength) 在Microsoft.AspNetCore.Mvc.Infrastructure.FileStreamResultExecutor.ExecuteAsync(ActionContext上下文,FileStreamResult结果) 在Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultAsync(IActionResult结果) 在Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResultFilterAsyncTFilter,TFilterAsync 在Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResultExecutedContext上下文) 在Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.ResultNext [TFilter,TFilterAsync](状态和下一个,范围和范围,对象和状态,布尔值和isCompleted) 在Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultFilters() 在Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter() 在Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext上下文) 在Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(状态和下一个,范围和范围,对象和状态,布尔值和已完成) 在Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync() 在Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync() 在Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext) 在Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext上下文)
请注意,它说流不支持阅读,这是可以的,因为我要求使用以下命令创建流:cloudBlockBlob.OpenWriteAsync()
,但正如您所见,我我没有做任何读取操作,我只是将流返回到控制台应用程序。
谢谢
Attilio
大家好,
最后我们写了以下内容:
public static class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseKestrel();
}
[Route("api/[controller]")]
[ApiController]
public class ValuesController : Controller
{
[HttpPost("upload")]
public async Task<IActionResult> Upload()
{
try
{
CloudBlobContainer vCloudBlobContainer = await GetCloudBlobContainer().ConfigureAwait(false);
string boundary = GetBoundary(Request.ContentType);
MultipartReader reader = new MultipartReader(boundary, Request.Body, 80 * 1024);
Dictionary<string, string> sectionDictionary = new Dictionary<string, string>();
MultipartSection section;
MyFile myFile;
while ((section = await reader.ReadNextSectionAsync().ConfigureAwait(false)) != null)
{
ContentDispositionHeaderValue contentDispositionHeaderValue = section.GetContentDispositionHeader();
if (contentDispositionHeaderValue.IsFormDisposition())
{
FormMultipartSection formMultipartSection = section.AsFormDataSection();
string value = await formMultipartSection.GetValueAsync().ConfigureAwait(false);
sectionDictionary.Add(formMultipartSection.Name, value);
}
else if (contentDispositionHeaderValue.IsFileDisposition())
{
myFile = JsonConvert.DeserializeObject<MyFile>(sectionDictionary.GetValueOrDefault(nameof(MyFile)));
if (myFile == default(object))
{
throw new InvalidOperationException();
}
CloudBlockBlob cloudBlockBlob = vCloudBlobContainer.GetBlockBlobReference(myFile.RelativePath);
Stream stream = await cloudBlockBlob.OpenWriteAsync().ConfigureAwait(false);
FileMultipartSection fileMultipartSection = section.AsFileSection();
await cloudBlockBlob.UploadFromStreamAsync(fileMultipartSection.FileStream).ConfigureAwait(false);
}
}
return Ok();
}
catch (Exception e)
{
throw e;
}
}
private string GetBoundary(string contentType)
{
if (contentType == null)
{
throw new ArgumentNullException(nameof(contentType));
}
string[] elements = contentType.Split(' ');
string element = elements.First(entry => entry.StartsWith("boundary="));
string boundary = element.Substring("boundary=".Length);
return HeaderUtilities.RemoveQuotes(boundary).Value;
}
private async Task<CloudBlobContainer> GetCloudBlobContainer()
{
const string connectionString = "[Your connection string]";
const string containerName = "container";
try
{
CloudStorageAccount.TryParse(connectionString, out CloudStorageAccount cloudStorageAccount);
CloudBlobClient cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient();
CloudBlobContainer cloudBlobContainer = cloudBlobClient.GetContainerReference(containerName);
if (await cloudBlobContainer.CreateIfNotExistsAsync().ConfigureAwait(false))
{
BlobContainerPermissions blobContainerPermission = new BlobContainerPermissions()
{
PublicAccess = BlobContainerPublicAccessType.Container
};
await cloudBlobContainer.SetPermissionsAsync(blobContainerPermission).ConfigureAwait(false);
}
return cloudBlobContainer;
}
catch (Exception)
{
throw;
}
}
}
internal static class Program
{
private const string filePath = @"D:\Test.txt";
private const string baseAddress = "http://localhost:5000";
private static async Task Main(string[] args)
{
Console.WriteLine($"Test starts at {DateTime.Now.ToString("o")}");
FileStream fileStream = new FileStream(filePath, FileMode.Open);
MyFile vFile = new MyFile()
{
Lenght = 0,
RelativePath = "Test.txt"
};
await UploadStream(vFile, fileStream).ConfigureAwait(false);
Console.WriteLine($"Test ends at {DateTime.Now.ToString("o")}");
Console.Write("Press any key to exit...");
Console.ReadKey();
}
private static async Task UploadStream(MyFile myFile, Stream stream)
{
try
{
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(baseAddress);
using (MultipartFormDataContent multipartFormDataContent = new MultipartFormDataContent())
{
multipartFormDataContent.Add(new StringContent(JsonConvert.SerializeObject(myFile), Encoding.UTF8, "application/json"), nameof(MyFile));
multipartFormDataContent.Add(new StreamContent(stream), "stream", nameof(MyFile));
HttpResponseMessage httpResult = await httpClient.PostAsync("api/values/upload", multipartFormDataContent).ConfigureAwait(false);
httpResult.EnsureSuccessStatusCode();
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
我们将管理大文件,所以我们有问题...
我们进行了以下测试:
.UseKestrel()
更改了代码行.UseKestrel(options => options.Limits.MaxRequestBodySize = null)
,并尝试上传相同的文件。更改此代码应该可以解决问题,但是客户端返回了System.NET.Sockets.SocketException(“由于线程退出或应用程序请求,I / O操作已中止”),而没有抛出异常。控制器。有什么主意吗?
答案 0 :(得分:2)
您在客户端中获得的流与您在api中返回的流不同。 mvc框架需要一个可读流才能获取内容并将其作为字节[]通过客户端的网络顶部发送。
您需要将所有需要的数据发送到您的api,以便能够写入天蓝色的blob流。
客户端
private static async Task Main(string[] args) // async main available in c# 7.1
{
Console.WriteLine($"Test starts at {DateTime.Now.ToString("o")}");
FileStream fileStream = new FileStream(@"C:\Windows10Upgrade\Windows10UpgraderApp.exe", FileMode.Open);
MyFile vFile = new MyFile()
{
Lenght = 0,
Path = "https://c2calrsbackup.blob.core.windows.net/containername/Windows10UpgraderApp.exe",
RelativePath = "Windows10UpgraderApp.exe"
};
await UploadStream(vFile, fileStream);
Console.WriteLine($"Test ends at {DateTime.Now.ToString("o")}");
Console.Write("Press any key to exit...");
Console.ReadKey();
}
private static async Task UploadStream(MyFile myFile, Stream stream)
{
try
{
using (HttpClient httpClient = new HttpClient()) // instance should be shared
{
httpClient.BaseAddress = new Uri("https://localhost:5000");
using (MultipartFormDataContent multipartFormDataContent = new MultipartFormDataContent())
{
multipartFormDataContent.Add(new StringContent(JsonConvert.SerializeObject(myFile), Encoding.UTF8, "application/json"), nameof(MyFile));
// Here we add the file to the multipart content.
// The tird parameter is required to match the `IsFileDisposition()` but could be anything
multipartFormDataContent.Add(new StreamContent(stream), "stream", "myfile");
HttpResponseMessage httpResult = await httpClient.PostAsync("api/uploaddownload/upload", multipartFormDataContent).ConfigureAwait(false);
httpResult.EnsureSuccessStatusCode();
// We don't need any result stream anymore
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
Api侧:
[HttpPost("upload")]
public async Task<IActionResult> GetUploadStream()
{
const string contentType = "application/octet-stream";
string boundary = GetBoundary(Request.ContentType);
MultipartReader reader = new MultipartReader(boundary, Request.Body, 80 * 1024);
Dictionary<string, string> sectionDictionary = new Dictionary<string, string>();
var memoryStream = new MemoryStream();
MultipartSection section;
while ((section = await reader.ReadNextSectionAsync()) != null)
{
ContentDispositionHeaderValue contentDispositionHeaderValue = section.GetContentDispositionHeader();
if (contentDispositionHeaderValue.IsFormDisposition())
{
FormMultipartSection formMultipartSection = section.AsFormDataSection();
string value = await formMultipartSection.GetValueAsync();
sectionDictionary.Add(formMultipartSection.Name, value);
}
else if (contentDispositionHeaderValue.IsFileDisposition())
{
// we save the file in a temporary stream
var fileMultipartSection = section.AsFileSection();
await fileMultipartSection.FileStream.CopyToAsync(memoryStream);
memoryStream.Position = 0;
}
}
CloudStorageAccount.TryParse(connectionString, out CloudStorageAccount cloudStorageAccount);
CloudBlobClient cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient();
CloudBlobContainer cloudBlobContainer = cloudBlobClient.GetContainerReference(containerName);
if (await cloudBlobContainer.CreateIfNotExistsAsync())
{
BlobContainerPermissions blobContainerPermission = new BlobContainerPermissions()
{
PublicAccess = BlobContainerPublicAccessType.Container
};
await cloudBlobContainer.SetPermissionsAsync(blobContainerPermission);
}
MyFile myFile = JsonConvert.DeserializeObject<MyFile>(sectionDictionary.GetValueOrDefault(nameof(MyFile)));
CloudBlockBlob cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference(myFile.RelativePath);
using(Stream blobStream = await cloudBlockBlob.OpenWriteAsync())
{
// Finally copy the file into the blob writable stream
await memoryStream.CopyToAsync(blobStream);
}
// you can replace OpenWriteAsync by
// await cloudBlockBlob.UploadFromStreamAsync(memoryStream);
return Ok(); // return httpcode 200
}
如果在else if
块内移动azureblog代码,则可以避免临时内存流。但是您需要确保FormData的顺序。 (然后是元数据文件)
答案 1 :(得分:0)
NotSupportedException
异常表示方法没有实现,您不应调用它。您不应处理该异常。取而代之的是,您应该做什么取决于异常的原因:实现是否完全不存在,或者成员调用是否与对象的目的不一致(例如在read-上调用FileStream.Read
方法)仅有FileStream
个对象。
您可以参考以下代码:
CloudBlockBlob cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference(myFile.RelativePath);
MemoryStream mem = new MemoryStream();
await cloudBlockBlob.DownloadToStreamAsync(mem);
mem.Position = 0;
return new FileStreamResult(mem, contentType);
有关更多详细信息,您可以参考此article。