Using statement in Task - c#

In an ASP.NET Web API controller I want to return an image. For streaming the image I need a MemoryStream. Normally I would wrap it in a using statement in order to make sure it gets properly disposed afterwards. However, as this executes asynchronously in a Task this doesn't work:
public class ImagesController : ApiController
{
private HttpContent GetPngBitmap(Stream stream)
{
var pngBitmapEncoder = new PngBitmapEncoder();
pngBitmapEncoder.Save(stream);
stream.Seek(0, SeekOrigin.Begin);
return new StreamContent(stream);
}
// GET api/images
public Task<HttpResponseMessage> Get(string id, string path)
{
//do stuff
return Task.Factory.StartNew(() =>
{
var stream = new MemoryStream(); //as it's asynchronous we can't use a using statement here!
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = GetPngBitmap(stream)
};
response.Content.Headers.ContentType =
new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
return response;
}
//how can I dispose stream???
});
}
}

MemoryStream is one of the classes that implement IDisposable, because their base class does. MemoryStream doesn't hold any resources (apart from managed memory), so disposing it is actually unnecessary.
HttpResponseMessage is disposable. This means that when the whole response it sent, that object is disposed. Doing that disposes the contained HttpContent, which in the case of StreamContent disposes the underlying Stream. So even if you had a Stream that should be disposed, you don't have to worry about it in this case.

use "using" then it will disposed automatically.
public class ImagesController : ApiController
{
// GET api/images
public Task<HttpResponseMessage> Get(string id, string path)
{
//do stuff
return Task.Factory.StartNew(() =>
{
using( stream = new MemoryStream()) //as it's asynchronous we can't use a using statement here!
{
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = GetPngBitmap(stream)
};
response.Content.Headers.ContentType =
new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
return response;
}
//how can I dispose stream???
}
});
}
}

Maybe return a Tuple<HttpResponseMessage, Stream> and attach a continuation task to dispose of the Stream?
public Task<Tuple<HttpResponseMessage,Stream>> Get(string id, string path)
{
//do stuff
return Task.Factory.StartNew(() =>
{
var stream = new MemoryStream();
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = GetPngBitmap(stream)
};
response.Content.Headers.ContentType =
new System.Net.Http.Headers.MediaTypeHeaderValue("image/png");
return Tuple.Create(response, stream);
}
})
.ContinueWith(t => t.Result.Item2.Close());
}

Maybe this sounds naif, but can't you just dispose it at the end of the task?
var stream = new MemoryStream();
//use the stream...
stream.Dispose();
edit: or "Close()", it's the same on MemoryStream.

You can do it like this. This will execute your initialRun first and after the async call, return in and it will execute the continue with.
Task<System.IO.MemoryStream> initialRun = new Task<System.IO.MemoryStream>(() =>
{
System.IO.MemoryStream stream = new System.IO.MemoryStream();
// use stream
return stream;
});
initialRun.ContinueWith(new Action<Task<System.IO.MemoryStream>>((stream) =>
{
stream.Dispose();
}),
TaskScheduler.FromCurrentSynchronizationContext());
initialRun.Start();

Related

Stream Extensions to convert Stream content into String or Byte array

Using C# 10 I am creating Stream extensions to get content into a String or Byte array.
Something similar to File.ReadAllTextAsync in Microsoft's Net 6.
public static async Task<string> ReadAllTextAsync(this Stream stream). {
string result;
using (var reader = new StreamReader(stream)) {
result = await reader.ReadToEndAsync().ConfigureAwait(false);
}
return result;
}
public static async Task<byte[]> ReadAllBytesAsync(this Stream stream) {
using (var content = new MemoryStream()) {
var buffer = new byte[4096];
int read = await stream.ReadAsync(buffer, 0, 4096).ConfigureAwait(false);
while (read > 0) {
content.Write(buffer, 0, read);
read = await stream.ReadAsync(buffer, 0, 4096).ConfigureAwait(false);
}
return content.ToArray();
}
}
public static async Task<List<string>> ReadAllLinesAsync(this Stream stream) {
var lines = new List<string>();
using (var reader = new StreamReader(stream)) {
string line;
while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null) {
lines.Add(line);
}
}
return lines;
}
Is there a better way to do this?
I am not sure about the ConfigureAwait(false) that I picked on some code online.
A better alternative for the ReadAllBytesAsync is
public static async Task<byte[]> ReadAllBytesAsync(this Stream stream)
{
switch (stream)
{
case MemoryStream mem:
return mem.ToArray();
default:
using var m = new MemoryStream();
await stream.CopyToAsync(m);
return mem.ToArray();
}
}
For the ReadAllLinesAsync, the async stream in C# 8 can make the code cleaner:
public IAsyncEnumerable<string> ReadAllLinesAsync(this Stream stream)
{
using var reader = new StreamReader(stream)
while (await reader.ReadLineAsync() is { } line)
{
yield return line;
}
}
notice that the empty brace { } here is actually a property pattern that is only available after C# 8, it checks whether reader.ReadLineAsync() is null, if it's not, assign it to the line variable.
Usage:
var lines = await stream.ReadAllLinesAsync();
await foreach (var line in lines)
{
// write your own logic here
}
P.S.:
The ConfigureAwait(false) is kinda useless if your app is single-threaded like console apps, it instructs the awaiter not to capture the SynchronizationContext and let continuation run on the thread that runs the await statement, this method is useful when you're writing a library or SDK, since your user may use your library in a GUI application, and the combination of block waiting such as calling Task.Wait() and the capturing of SynchronizationContext often leads to deadlock, and ConfigureAwait(false) solves this. For detail explanation see ConfigureAwait FAQ

JToken.WriteToAsync does not write to JsonWriter

I'm trying to create a middleware that changes the request in a certain way. I am able to read it and change the content but I cannot figure out how to correctly setup the stream writers to create a new body. When I call normalized.WriteToAsync(jsonWriter) the MemoryStream remains empty and consequently I receive the A non-empty request body is required. exception. What am I missing here? This is what I have so far:
public async Task Invoke(HttpContext context)
{
if (context.Request.ContentType == "application/json" && context.Request.ContentLength > 0)
{
using var scope = _logger.BeginScope("NormalizeJson");
try
{
using var requestReader = new HttpRequestStreamReader(context.Request.Body, Encoding.UTF8);
using var jsonReader = new JsonTextReader(requestReader);
var json = await JToken.LoadAsync(jsonReader);
var normalized = _normalize.Visit(json); // <-- Modify json and return JToken
// Create new Body
var memoryStream = new MemoryStream();
var requestWriter = new StreamWriter(memoryStream);
var jsonWriter = new JsonTextWriter(requestWriter);
await normalized.WriteToAsync(jsonWriter); // <-- At this point the MemoryStream has still 0 length.
var content = new StreamContent(memoryStream.Rewind()); // <-- Use helper extension to Seek.Begin = 0
context.Request.Body = await content.ReadAsStreamAsync();
}
catch (Exception e)
{
_logger.Scope().Exceptions.Push(e);
}
}
await _next(context);
}
Demo for LINQPad etc.:
async Task Main()
{
var token = JToken.FromObject(new User { Name = "Bob" });
var memoryStream = new MemoryStream();
var requestWriter = new StreamWriter(memoryStream);
var jsonWriter = new JsonTextWriter(requestWriter);
await token.WriteToAsync(jsonWriter);
memoryStream.Length.Dump(); // <-- MemoryStream.Length = 0
}
public class User
{
public string Name { get; set; }
}
You need to properly flush and close your JsonTextWriter and StreamWriter in order to fully populate the memoryStream, like so:
var memoryStream = new MemoryStream();
// StreamWriter implements IAsyncDisposable
// Leave the underlying stream open
await using (var requestWriter = new StreamWriter(memoryStream, leaveOpen: true))
{
var jsonWriter = new JsonTextWriter(requestWriter); // But JsonTextWriter does not implement IAsyncDisposable, only IDisposable!
try
{
await token.WriteToAsync(jsonWriter);
}
finally
{
await jsonWriter.CloseAsync();
}
}
Demo fiddle #1 here.
Or, since you're writing to a MemoryStream, there's really no nead to use async at all, and instead you can do:
var memoryStream = new MemoryStream();
using (var requestWriter = new StreamWriter(memoryStream, leaveOpen: true)) // Leave the underlying stream open
using (var jsonWriter = new JsonTextWriter(requestWriter))
{
token.WriteTo(jsonWriter);
}
Demo fiddle #2 here.
Notes:
Note the use of await using for the StreamWriter. This syntax guarantees that the StreamWriter will be flushed and closed asynchronously, and can be used on any object that implements IAsyncDisposable. (This only really matters if you were writing to a file stream or other non-memory stream.)
It seems that neither JsonTextWriter nor the base class JsonWriter implement IAsyncDisposable, so I had to asynchronously close the JSON writer manually rather than via a using statement. The outer await using should ensure that the underlying StreamWriter is not left open in the event of an exception.
JSON RFC 8259 specifies that Implementations MUST NOT add a byte order mark (U+FEFF) to the beginning of a networked-transmitted JSON text. Thus, when constructing a StreamWriter, it is recommended to pass an encoding such as new UTF8Encoding(false) that does not prepend a BOM. Alternatively, if you just want UTF-8, the StreamWriter constructors will create a StreamWriter with UTF-8 encoding without a Byte-Order Mark (BOM) if you do not specify one yourself and leave a default value for that parameter as is shown in the code above.

How to download a png file with ASP.NET Core

I need the user to be able to download png images from my site. When the mthod runs it completes without errors but no image is downloaded. I do not need the user to see a pop-up dialog thought it is certainly helpful. This is what I have right now:
public async Task<IActionResult> DownloadImage(string filename)
{
var path = Path.GetFullPath("./wwwroot/images/school-assets/" + filename);
MemoryStream memory = new MemoryStream();
using (FileStream stream = new FileStream(path, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
return File(memory, "image/png", "download");
}
This method is called by an ajax call in the view that looks like this
$.ajax({
url: "./MyHome/DownloadImage",
type: "Get",
data: {filename : filename},
success: function (file) {
},
error: function (request, status, error) {
console.log(request.responseText);
}
});
}
Edit:
If i console.log file in the success portion i see a string of bytes so I know it is creating the file but not letting the user get to i. I have tried content disposition and creating a physical file result as suggested.
For File, you need to provide the file name with file extension, otherwise, the downloaded file will not be able to open.
Try something like
public async Task<IActionResult> DownloadImage(string filename)
{
var path = Path.GetFullPath("./wwwroot/images/school-assets/" + filename);
MemoryStream memory = new MemoryStream();
using (FileStream stream = new FileStream(path, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
return File(memory, "image/png", Path.GetFileName(path));
}
You need to set the content dispositon type to enable direct downloading of the file :
public IActionResult OnGetPng()
{
var bytes = System.IO.File.ReadAllBytes("test.png");
var cd = new System.Net.Mime.ContentDisposition
{
FileName = "test.png",
Inline = false
};
Response.Headers.Add("Content-Disposition", cd.ToString());
Response.Headers.Add("X-Content-Type-Options", "nosniff");
return File(bytes, "image/png");
}
If you prefer you can also make use of the PhysicalFileResult type which takes care of your stream and return FileResult from your controller. In that case your code looks like this:
var fn = Path.Combine(env.WebRootPath, "test.png");
var contentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
Response.Headers[HeaderNames.ContentDisposition] = contentDisposition.ToString();
return new PhysicalFileResult(fn, "image/jpeg");
To get access to the WebRootPath you have to inject IHostingEnvironment env into your constructor.
#Caleb sir from below code you can download png file.
Download png file from folder
[HttpGet]
public FileStreamResult DownloadPngFile(string fileName)
{
var stream = new FileStream(Directory.GetCurrentDirectory() + "\\wwwroot\\images\\school-assets\\" + fileName, FileMode.Open);
return new FileStreamResult(stream, "image/png");
}
Download png file from database
[HttpGet]
public FileStreamResult DownloadPngFileFromDataBase(string id)
{
var _fileUpload = _db.ImageFileUpload.SingleOrDefault(aa => aa.fileid == id);
// _fileUpload.FileContent column type is byte
MemoryStream ms = new MemoryStream(_fileUpload.FileContent);
return new FileStreamResult(ms, "image/png");
}
For more info please also see this question and answer. Download Pdf file in asp.net core (accepted answer) and one more extra link
Download files in asp.net core
This code can save photos from URL addresses in the server folder.
private readonly Lazy<HttpClient> _client;
In constructor:
_client = new Lazy<HttpClient>(() => clientFactory.CreateClient());
That is better to use lazy loading in a way the server will not spend additional resources to create HttpClient immediately.
public async Task<string> SavePhotoInFolder(string url)
{
string photoPath = $"/Photos/{Guid.NewGuid()}.jpeg";
using (var request = new HttpRequestMessage(HttpMethod.Get, url))
using (
Stream contentStream = await (await _client.Value.SendAsync(request)).Content.ReadAsStreamAsync(),
stream = new FileStream($"{_appEnvironment.WebRootPath}{photoPath}", FileMode.Create))
{
await contentStream.CopyToAsync(stream);
}
return photoPath;
}
You can use HttpClient
using (var client = new HttpClient())
{
try
{
using var result = await client.GetAsync($"http://{url}");
if (result.IsSuccessStatusCode)
{
return await result.Content.ReadAsByteArrayAsync();
}
}
catch(Exception ex)
{
Console.WriteLine(ex.InnerException);
}
}

Return stream immediately and then write to stream asynchronously

In my current code I have a method like this to read data from a device (pseudo code):
public async Task<string> ReadAllDataFromDevice()
{
var buffer = "";
using (var device = new Device())
{
while(device.HasMoreData)
{
buffer += await device.ReadLineAsync();
}
}
return buffer;
}
I then want to send all that data via the network to some receiver. The amount of data can be really large. So clearly the above design is not very memory-efficient since it requires to read all the data before I can start sending it to the network socket.
So what I'd like to have is a function that returns a stream instead. Something like this:
public async Task<Stream> ReadAllDataFromDevice()
{
var stream = new MemoryStream();
using (var device = new Device())
using (var streamWriter = new StreamWriter(stream, new UTF8Encoding(), 512, true))
{
while(device.HasMoreData)
{
var line = await device.ReadLineAsync();
await streamWriter.WriteLineAsync(line);
}
await streamWriter.FlushAsync();
}
return stream;
}
This returns a stream but it clearly does not solve my problem, because the stream is returned only after all the data has been read from the device.
So I came up with this:
public Stream ReadAllDataFromDevice()
{
var stream = new MemoryStream();
Task.Run(async () => {
using (var device = new Device())
using (var streamWriter = new StreamWriter(stream, new UTF8Encoding(), 512, true))
{
while(device.HasMoreData)
{
var line = await device.ReadLineAsync();
await streamWriter.WriteLineAsync(line);
}
await streamWriter.FlushAsync();
}
});
return stream;
}
Is this a good design? I'm especially concerned about thread-safety, lifetime of the stream object used in the lambda, and exception handling.
Or is there a better pattern for this kind of problem?
Edit
Actually I just came up with another design that looks much cleaner to me. Instead of having the ReadAllDataFromDevice() function returning a stream, I let the consumer of the data provide the stream, like this:
public async Task ReadAllDataFromDevice(Stream stream)
{
using (var device = new Device())
using (var streamWriter = new StreamWriter(stream, new UTF8Encoding(), 512, true))
{
while(device.HasMoreData)
{
var line = await device.ReadLineAsync();
await streamWriter.WriteLineAsync(line);
}
await streamWriter.FlushAsync();
}
}
This is the design I'm using now:
public async Task ReadAllDataFromDevice(Func<Stream, Task> readCallback)
{
using (var device = new Device())
{
await device.Initialize();
using (var stream = new DeviceStream(device))
{
await readCallback(stream);
}
}
}
The line-by-line device access is encapsulated in the custom DeviceStream class (not shown here).
The consumer of the data would look something like this:
await ReadAllDataFromDevice(async stream => {
using (var streamReader(stream))
{
var data = await streamReader.ReadToEndAsync();
// do something with data
}
});

Does a MemoryStream get disposed of automatically when returning it as an ActionResult?

public ActionResult CustomChart(int reportID)
{
Chart chart = new Chart();
// Save the chart to a MemoryStream
var imgStream = new MemoryStream();
chart.SaveImage(imgStream);
imgStream.Seek(0, SeekOrigin.Begin);
// Return the contents of the Stream to the client
return File(imgStream, "image/png");
}
I am accustomed to using the 'using' statement in conjuction with MemoryStreams. Is this a scenario where the 'using' statement is not necessary? Or is it valid to call return inside of a 'using' statement?
EDIT:
For my purposes I have found that the introduction of a 'using' statement does NOT work (throws an ObjectDisposedException). Here's what I'm doing with it client-side:
$('#ReportTest').bind('load', function () {
$('#LoadingPanel').hide();
$(this).unbind('load');
}).bind('error', function () {
$('#LoadingPanel').hide();
$(this).unbind('error');
}).attr('src', '../../Chart/CustomChart?ReportID=' + settings.id);
Does a MemoryStream get disposed of automatically when returning it as an ActionResult?
Yes, MVC (at least version 3) will clean it up for you. You can take a look at the source of the WriteFile method in FileStreamResult:
protected override void WriteFile(HttpResponseBase response) {
// grab chunks of data and write to the output stream
Stream outputStream = response.OutputStream;
using (FileStream) {
byte[] buffer = new byte[_bufferSize];
while (true) {
int bytesRead = FileStream.Read(buffer, 0, _bufferSize);
if (bytesRead == 0) {
// no more data
break;
}
outputStream.Write(buffer, 0, bytesRead);
}
}
}
The line using (FileStream) { will place the Stream in a using block, thus Disposing of it when it has written the contents to the Http Response.
You can also verify this behavior by creating a dummy stream that does this:
public class DummyStream : MemoryStream
{
protected override void Dispose(bool disposing)
{
Trace.WriteLine("Do I get disposed?");
base.Dispose(disposing);
}
}
So MVC will dispose it.
Sean: Do NOT use 'using' as it will Dispose the object. Leaving MVC accessing a Disposed object. Hence the exception(server error) you experienced is certainly an ObjectDisposedException. The WriteFile function previously posted Disposes the object for you.
A MemoryStream is not necessary in this situation. You can avoid it by creating a Custom ActionResult like this :
public class ChartResult : ActionResult
{
private Chart _chart;
public ChartResult(Chart chart)
{
if (chart == null)
throw new ArgumentNullException("chart");
_chart = chart;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = "image/png";
response.BufferOutput = false;
_chart.ImageType = ChartImageType.Png;
_chart.SaveImage(response.OutputStream);
}
}
The following is valid code that disposes of the stream. If enclosed in a using block the MemoryStream.Dispose() method will be called automatically when returning.
public ActionResult CustomChart(int reportID)
{
Chart chart = new Chart();
using (var imgStream = new MemoryStream()) {
chart.SaveImage(imgStream);
imgStream.Seek(0, SeekOrigin.Begin);
return File(imgStream, "image/png");
}
}
You can achieve the same result by putting the object inside a try block and then calling Dispose in a finally block. In fact, according to the MSDN documentation this is how the using statement is translated by the compiler. And in a try..finally block the finally will always be executed even when try exits through return.
The compiler will translate the using block to the following:
MemoryStream imgStream = new MemoryStream();
try
{
chart.SaveImage(imgStream);
imgStream.Seek(0, SeekOrigin.Begin);
return File(imgStream, "image/png");
}
finally
{
if (imgStream != null)
((IDisposable)imgStream).Dispose();
}

Categories