Post file content using HttpClient and get result - c#

I have posted a text file to action method of me web api using formdata. When I am sending text file it is working fine but when I am sending image file or video file or audio file it is not working.
Post Call:
using (var client = new HttpClient())
{
using (var formData = new MultipartFormDataContent())
{
HttpContent fileStreamContent = new StreamContent(new MemoryStream(fileStream));
formData.Add(fileStreamContent, "fileForUpload");
using (var response = await client.PostAsync("http://localhost:56537/api/CreateContainer/UploadBlobs", formData))
{
if (response.IsSuccessStatusCode)
{
var productJsonString = await response.Content.ReadAsStringAsync();
}
}
}
}
At action method:
try
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
var file = provider.Contents[0];
objAzureFunctionClass.UploadBlobsToContainer(file.ReadAsStringAsync().Result, filenametoUpload, containerNametoUpload);
return false;
}
public bool UploadBlobsToContainer(string fileStream, string filenametoUpload, string containerNametoUpload)
{
try
{
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference(containerNametoUpload);
CloudBlockBlob blockBlob = container.GetBlockBlobReference(filenametoUpload);
blockBlob.UploadFromStream(GenerateStreamFromString(fileStream));
return true;
}
catch (Exception ex)
{
throw ex;
}
}
private Stream GenerateStreamFromString(String p)
{
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(p);
writer.Flush();
stream.Position = 0;
return stream;
}

Related

Request with file to web API

I have a problem with the application sending the file to the web service. This is my endpoint/controller.
[HttpPost]
public async Task<IActionResult> Post(List<IFormFile> files)
{
long size = files.Sum(f => f.Length);
foreach (var formFile in files)
{
if (formFile.Length > 0)
{
var filePath = "C:\\Files\\TEST.pdf";
using (var stream = System.IO.File.Create(filePath))
{
await formFile.CopyToAsync(stream);
}
}
}
This controller works fine in Postman.
and this is my application that makes the request:
byte[] bytes = System.IO.File.ReadAllBytes("C:\\Files\\files.pdf");
Stream fileStream = File.OpenRead("C:\\Files\\files.pdf");
HttpContent bytesContent = new ByteArrayContent(bytes);
using (var client = new HttpClient())
using (var formData = new MultipartFormDataContent())
{
formData.Add(bytesContent,"file", "files.pdf");
try
{
var response = await client.PostAsync(url, formData);
}catch(Exception ex)
{
Console.WriteLine(ex);
}
It doesn't work. I'm not receiving the file in controller. I also tried this:
string fileToUpload = "C:\\Files\\files.pdf";
using (var client = new WebClient())
{
byte[] result = client.UploadFile(url, fileToUpload);
string responseAsString = Encoding.Default.GetString(result);
}
but the result is the same. Would you be able to help?
Update 15/09/2020
This is the upload codes in ConsoleApplication. And it works with small file but not large file.
public static async Task upload(string url)
{
//const string url = "https://localhost:44308/file/post";
const string filePath = "C:\\Files\\files.pdf";
try {
using (var httpClient = new HttpClient{
Timeout = TimeSpan.FromSeconds(3600)
})
{
using (var form = new MultipartFormDataContent())
{
using (var fs = System.IO.File.OpenRead(filePath))
{
fs.Position = 0;
using (var streamContent = new StreamContent(fs))
{
form.Add(streamContent, "files", Path.GetFileName(filePath));
HttpResponseMessage response = httpClient.PostAsync(url, form).Result;
fs.Close();
}
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
There are two steps to fix your problem.
1.Add ContentType to Headers
bytesContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
2. The file parameter name in formData should match action parameter name.
formData.Add(bytesContent,"file", "files.pdf"); //should be files
public async Task<IActionResult> Post(List<IFormFile> files)
Update
HttpClient.PostAsync() doesn't work when awaited in Console Application. Instead of blocking with .Result, use .GetAwaiter().GetResult().
HttpResponseMessage response = httpClient.PostAsync(url, form).Result;
Here is the code to show how to upload file.
Codes of Controller
public class FileController : Controller
{
[HttpPost]
public async Task<IActionResult> Post(List<IFormFile> files)
{
long size = files.Sum(f => f.Length);
foreach (var formFile in files)
{
if (formFile.Length > 0)
{
var filePath = "C:\\Files\\TEST.pdf";
using (var stream = System.IO.File.Create(filePath))
{
await formFile.CopyToAsync(stream);
}
}
}
return Ok();
}
[HttpGet]
public async Task<IActionResult> upload()
{
const string url = "https://localhost:44308/file/post";
const string filePath = #"C:\\Files\\files.pdf";
using (var httpClient = new HttpClient())
{
using (var form = new MultipartFormDataContent())
{
using (var fs = System.IO.File.OpenRead(filePath))
{
using (var streamContent = new StreamContent(fs))
{
using (var fileContent = new ByteArrayContent(await streamContent.ReadAsByteArrayAsync()))
{
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
// "file" parameter name should be the same as the server side input parameter name
form.Add(fileContent, "files", Path.GetFileName(filePath));
HttpResponseMessage response = await httpClient.PostAsync(url, form);
}
}
}
}
}
return Ok();
}
}
Test

Http client Post with body parameter and file in c#

I was trying to attach a csv file as a body parameter in my test script. But still as per the below code controller expect file and just curious how should I pass that.
I run test script in below order
Method-1
public void AttachedRatesFile(string fileName)
{
_form = string.IsNullOrWhiteSpace(fileName)
? _form = new StringContent(string.Empty)
: _form = new StreamContent(File.OpenRead($"{ResourceFolder}{fileName}"));
_form.Headers.ContentType = new MediaTypeHeaderValue("application/csv");
_form.Headers.ContentDisposition = new ContentDispositionHeaderValue(fileName);
}
Method-2
public void PostRequestExecutes(string resource)
{
var content = new MultipartFormDataContent{_form};
WhenThePostRequestExecutesWithContent(resource, content);
}
Method-3
public async void WhenThePostRequestExecutesWithContent(string resource, HttpContent content)
{
ResponseMessage = await HttpClient.PostAsync(resource, content);
}
I see null in below file parameter
Controller:
public async Task<IActionResult> SeedData(IFormFile file)
{
var result = await _seedDataService.SeedData(file);
return Ok(new { IsUploadSuccesful = result});
}
I would add that to the body as a stream
var memoryContentStream = new MemoryStream();
using (var streamWriter = new StreamWriter(memoryContentStream, Encoding.UTF8, 1000,
true))
{
using (var jsonTextWriter = new JsonTextWriter(streamWriter))
{
var jsonSerializer = new JsonSerializer();
jsonSerializer.Serialize(jsonTextWriter, OBJECT);
jsonTextWriter.Flush();
}
}
if (memoryContentStream.CanSeek)
{
memoryContentStream.Seek(0, SeekOrigin.Begin);
}
Then
using (var streamContent = new StreamContent(memoryContentStream))
{
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
request.Content = streamContent;
using (var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
{
var stream = await response.Content.ReadAsStreamAsync();
response.EnsureIsSuccessStatusCode();
}
}
The above would first write the content as a memory stream and then when creating the POST request you can send the stream as a streamContent

UPDATE [2] - ASP.NET Core Web API upload and download file - Stream exception

This question is related to: ASP.NET Core Web API upload and download file
First, I would like to thank Powel Gerr who helped me to understand some nuances with his post (http://weblogs.thinktecture.com/pawel/2017/03/aspnet-core-webapi-performance.html). I still have a problem to solve.
My scenario
Suppose we have a .NET Core console application:
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;
}
As you can see, MyFile is a support class that contains some information. On the other hand, the controller is as follows:
[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);
}
The problem
When the controller returns with the instruction return new FileStreamResult(streamResult, contentType); the following exception is generated in the controller itself (not in the calling console app):
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware:Error: An unhandled exception has occurred while executing the request.
System.NotSupportedException: Stream does not support reading.
at System.IO.Stream.BeginReadInternal(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state, Boolean serializeAsynchronously, Boolean apm)
at System.IO.Stream.BeginEndReadAsync(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.Stream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Http.Extensions.StreamCopyOperation.CopyToAsync(Stream source, Stream destination, Nullable`1 count, Int32 bufferSize, CancellationToken cancel)
at Microsoft.AspNetCore.Mvc.Infrastructure.FileResultExecutorBase.WriteFileAsync(HttpContext context, Stream fileStream, RangeItemHeaderValue range, Int64 rangeLength)
at Microsoft.AspNetCore.Mvc.Infrastructure.FileStreamResultExecutor.ExecuteAsync(ActionContext context, FileStreamResult result)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultAsync(IActionResult result)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResultFilterAsyncTFilter,TFilterAsync
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResultExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeResultFilters()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
Please note that it says Stream does not support reading and this is ok because I ask to create the stream with the following command: cloudBlockBlob.OpenWriteAsync(), but as you can see, I'm not doing any reading operation and I'm only returning the stream to the console app.
Questions
What do you think it can be? Is there any hidden reading operation that I'm not aware of?
How to solve the problem?
Thank you,
Attilio
UPDATE
Hi all,
finally we wrote the following:
Controller
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;
}
}
}
Client
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);
}
}
}
We'll manage big files, so we have a problem...
Tests
We did the following tests:
we tried to upload "small" files (less than 30000000 bytes) and all worked fine.
we tried to upload "big" files (more than 30000000 bytes) and the controller returned a Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException ("Request body too large.").
we changed the line of code .UseKestrel() with .UseKestrel(options => options.Limits.MaxRequestBodySize = null) and tried to upload the same file. This code change should have to solve the problem but the client returned a System.NET.Sockets.SocketException ("The I/O operation has been aborted because of either a thread exit or an application request") while no exception was thrown in the controller.
Any idea?
The stream you get in the client is not the same the one you return in your api. The mvc framework need a readable stream to be able to get the content and send it as byte[] throught the network top the client.
You need to send all requiered data to your api to be able to write into the azure blob stream.
Client side
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 side :
[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
}
See https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads#uploading-large-files-with-streaming for documentation
You can avoid the temporary memory stream if you move azureblog code inside the else if block. But you need to ensure the order of FormData. (Metadata then file)
The NotSupportedException exception indicates that a method has no implementation and that you should not call it. You should not handle the exception. Instead, what you should do depends on the cause of the exception: whether an implementation is completely absent , or the member invocation is inconsistent with the purpose of an object (such as a call to the FileStream.Read method on a read-only FileStream object .
You could refer to the following code:
CloudBlockBlob cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference(myFile.RelativePath);
MemoryStream mem = new MemoryStream();
await cloudBlockBlob.DownloadToStreamAsync(mem);
mem.Position = 0;
return new FileStreamResult(mem, contentType);
For more details, you could refer to this article.

ObjectDisposedException: The CancellationTokenSource has been disposed

I'm starting to develop with Xamarin.Forms using Xamarin Studio on my MacBook Pro. I built an application whose purpose is to query a PrestaShop Website, retrieve the Products and show them.
When deploying the application to Android, I had some problems with versions below Marshmallow, but I solved them, so I won't describe them here.
When deploying the application to iOS (Simulator), I'm still having a critical problem. The application runs, but when I click on the button to retrieve the data, it crashes giving me a System.ObjectDisposedException, whose message is "The CancellationTokenSource has been disposed". I'll paste here the relevant source code:
async void button1_Click(object sender, System.EventArgs e)
{
try
{
HttpClientHandler hnd = new HttpClientHandler();
hnd.Credentials = new NetworkCredential("[apikey]", "");
string res;
using (var client = new HttpClient(hnd))
{
var responseText = await client.GetStringAsync("[endpoint]/products");
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(responseText.Replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", string.Empty));
writer.Flush();
stream.Position = 0;
XDocument doc = XDocument.Load(stream);
res = JsonConvert.SerializeXNode(doc, Formatting.Indented, true);
}
var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(res);
//Result.Text = data["products"].GetType().ToString() + Result.Text;
Func<string, Task> creator_method = async (string id) =>
{
try
{
var responseProd = await client.GetStringAsync($"[endpoint]/products/{id}"); // AT THIS ROW THE EXCEPTION IS RAISED!!!
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(responseProd.Replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", string.Empty));
writer.Flush();
stream.Position = 0;
XDocument doc = XDocument.Load(stream);
res = JsonConvert.SerializeXNode(doc, Formatting.Indented, true);
}
var product_rawData = JsonConvert.DeserializeObject<Dictionary<string, object>>(res);
var productData = (JObject)product_rawData["product"];
try
{
views.Children.Add(new ProductXaml(productData["name"]["language"]["#cdata-section"].ToString(), float.Parse(productData["price"]["#cdata-section"].ToString().Replace('.', ',')), productData["id_default_image"]["#xlink:href"].ToString()));
}
catch (NullReferenceException nre)
{
views.Children.Add(new ProductXaml(productData["name"]["language"]["#cdata-section"].ToString(), float.Parse(productData["price"]["#cdata-section"].ToString().Replace('.', ',')), ""));
}
}
catch (Exception gex) { throw; }
};
foreach (var product in ((JObject)data["products"])["product"])
try
{
Device.BeginInvokeOnMainThread(async () => { await creator_method.Invoke(product["#id"].ToString()); });
}
catch (TaskCanceledException tce) { continue; }
catch (WebException we) { continue;}
}
}
catch (HttpRequestException ex)
{
Result.Text = ex.Message;
}
}
How could I solve the problem?
"The CancellationTokenSource has been disposed"
It seems that your using (var client = new HttpClient())...'s CancellationToken has already been consumed by the first client.GetStringAsync() call.
How To Fix This
I would suggest putting your second call in its own using block, to avoid this.
Here's what your code should now look like, with a second using statement:
async void button1_Click(object sender, System.EventArgs e)
{
try
{
HttpClientHandler hnd = new HttpClientHandler();
hnd.Credentials = new NetworkCredential("[apikey]", "");
string res;
using (var client = new HttpClient(hnd))
{
var responseText = await client.GetStringAsync("[endpoint]/products");
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(responseText.Replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", string.Empty));
writer.Flush();
stream.Position = 0;
XDocument doc = XDocument.Load(stream);
res = JsonConvert.SerializeXNode(doc, Formatting.Indented, true);
}
var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(res);
//Result.Text = data["products"].GetType().ToString() + Result.Text;
Func<string, Task> creator_method = async (string id) =>
{
try
{
using (var newClient = new HttpClient(hnd)) // Create a new client to avoid your other one's token being consumed
{
var responseProd = await newClient.GetStringAsync($"[endpoint]/products/{id}");
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(responseProd.Replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", string.Empty));
writer.Flush();
stream.Position = 0;
XDocument doc = XDocument.Load(stream);
res = JsonConvert.SerializeXNode(doc, Formatting.Indented, true);
}
var product_rawData = JsonConvert.DeserializeObject<Dictionary<string, object>>(res);
var productData = (JObject)product_rawData["product"];
try
{
views.Children.Add(new ProductXaml(productData["name"]["language"]["#cdata-section"].ToString(), float.Parse(productData["price"]["#cdata-section"].ToString().Replace('.', ',')), productData["id_default_image"]["#xlink:href"].ToString()));
}
catch (NullReferenceException nre)
{
views.Children.Add(new ProductXaml(productData["name"]["language"]["#cdata-section"].ToString(), float.Parse(productData["price"]["#cdata-section"].ToString().Replace('.', ',')), ""));
}
}
}
catch (Exception gex) { throw; }
};
foreach (var product in ((JObject)data["products"])["product"])
try
{
Device.BeginInvokeOnMainThread(async () => { await creator_method.Invoke(product["#id"].ToString()); });
}
catch (TaskCanceledException tce) { continue; }
catch (WebException we) { continue;}
}
}
catch (HttpRequestException ex)
{
Result.Text = ex.Message;
}
}
Hope this helps! :)
Create a new instance of HttpClient like this
public static HttpClient GetHttpClient()
{
HttpClient httpClient = new HttpClient()
{
BaseAddress = new Uri(BaseUrl)
};
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
return httpClient;
}
I was having the same issue, I was using a single httpclient for all my api requests.
private static HttpClient httpClient;
public static HttpClient GetHttpClient()
{
if (httpClient == null)
{
httpClient = new HttpClient()
{
BaseAddress = new Uri(BaseUrl)
};
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
}
return httpClient;
}
It seems that your using (var client = new HttpClient())...'s
CancellationToken has already been consumed by the first
client.GetStringAsync() call.
Geoff 's answer I removed my static httpclient and now creates a new httpclient for all requests, it's fixed my issue.
public static HttpClient GetHttpClient()
{
HttpClient httpClient = new HttpClient()
{
BaseAddress = new Uri(BaseUrl)
};
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
return httpClient;
}

How to return a file from web api controller?

I'm using a MVC 5 web Api Controller, and I want to return a file:
[Route("")]
public HttpResponseMessage GetFile()
{
var statusCode = HttpStatusCode.OK;
FileStream file = XLGeneration.XLGeneration.getXLFileExigence();
return Request.CreateResponse(statusCode, file);
}
It dosn't work.
The exception from postman is:
"ExceptionMessage": "The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'."
I'm posting what worked for me as an alternative in case anybody else is having trouble.
[ActionName("File")]
[HttpGet]
public HttpResponseMessage File()
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new System.IO.FileStream(yourFilePath, System.IO.FileMode.Open);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
return response;
}
I returned byte[] from WebAPI controller and download PDF successfully.
I'm using iTextSharp (LGPL) 4.1.6 free PDF converter.
To install iTextSharp (LGPL / MPL), run the following command in the Package Manager Console.
Install-Package iTextSharp-LGPL -Version 4.1.6
Server side code
[Route("Export/ExportToPdf")]
public byte[] ExportToPdf(string html)
{
MemoryStream msOutput = new MemoryStream();
TextReader reader = new StringReader(html);
Document document = new Document(new Rectangle(842, 595));
PdfWriter writer = PdfWriter.GetInstance(document, msOutput);
document.Open();
document.HtmlStyleClass = #"<style>*{ font-size: 8pt; font-family:arial;}</style>";
var parsedHtmlElements = HTMLWorker.ParseToList(new StringReader(html), null);
foreach (var htmlElement in parsedHtmlElements)
{
document.Add(htmlElement as IElement);
}
document.Close();
return msOutput.ToArray();
}
Client Side Code.
//Call this function inside of AJAX success.
function ExportToPDF(data) {
//base64 To ArrayBuffer
var binaryString = window.atob(data);
var binaryLen = binaryString.length;
var bytes = new Uint8Array(binaryLen);
for (var i = 0; i < binaryLen; i++) {
var ascii = binaryString.charCodeAt(i);
bytes[i] = ascii;
}
//-------
var link = document.createElement('a');
link.href = window.URL.createObjectURL(new Blob([bytes], { type: 'application/pdf' }));
link.download = "Report.pdf";
link.click();
}
Try this...
[Route("")]
public HttpResponseMessage GetFile()
{
var result = new HttpResponseMessage(HttpStatusCode.OK);
try
{
var file = XLGeneration.XLGeneration.getXLFileExigence();
result.Content = new StreamContent(file);
result.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
var value = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
value.FileName = "Whatever your filename is";
result.Content.Headers.ContentDisposition = value;
}
catch (Exception ex)
{
// log your exception details here
result = new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
return result;
}
This should actually stream it back as a file.
Just idea:
public HttpResponseMessage GetFile()
{
FileStream file = XLGeneration.XLGeneration.getXLFileExigence();
using(var sr = new StreamReader(file))
{
content = sr.ReadToEnd();
return new HttpResponseMessage
{
Content = new StringContent(content, Encoding.UTF8, "application/json")
};
}
}

Categories

Resources