Giter Site home page Giter Site logo

Comments (23)

jchannon avatar jchannon commented on May 4, 2024 1

Might not be able to, just a brain dump for now 😄

from carter.

bazwilliams avatar bazwilliams commented on May 4, 2024 1

This works perfectly for streams which can be processed in memory, but unfortunately in my case, this doesn't fix the issue. I'm using a producer/consumer pattern to minimise the memory requirements of my service - the data coming from the database could be many hundreds of MB and I'd rather stream it directly to the client.

This means, I'm not likely to have the entire contents of the stream in memory at once and is the cause of the block - StreamCopyOperation.CopyToAsync won't start copying until the source stream has completed.

The solution (with and without hacky delay) doesn't work for streams larger than my producer/consumer buffer size. :(

from carter.

JoeStead avatar JoeStead commented on May 4, 2024

There's a SendFileAsync extension on HttpResponse that looks like it may be what you want: https://github.com/aspnet/HttpAbstractions/blob/dev/src/Microsoft.AspNetCore.Http.Extensions/SendFileResponseExtensions.cs#L25

I haven't got time at the moment to check 100%, will try to in a few hours

from carter.

bazwilliams avatar bazwilliams commented on May 4, 2024

I had a look at that, but I need an IFileInfo object to use that. I'm streaming data straight from a database so it isn't hitting the filesystem.

from carter.

JoeStead avatar JoeStead commented on May 4, 2024

Ah ok.

In which case, you might be able to do something similar to the internal operation here: https://github.com/aspnet/HttpAbstractions/blob/dev/src/Microsoft.AspNetCore.Http.Extensions/SendFileResponseExtensions.cs#L164

e.g.:
await StreamCopyOperation.CopyToAsync(fs, res.Body, null, CancellationToken.None);

If this does the job, then I think Botwin should probably have a nicer wrapper around it.

from carter.

jchannon avatar jchannon commented on May 4, 2024

from carter.

bazwilliams avatar bazwilliams commented on May 4, 2024

That is very similar to what I had before in Nancy - I didn't realise I could return a Response in a handler.

from carter.

bazwilliams avatar bazwilliams commented on May 4, 2024

@JoeStead Your suggestion is promising. My module now ends with:

var cd = new ContentDisposition { FileName = "journal-{type}.csv" };
res.Headers.Add("Content-Disposition", cd.ToString());
res.ContentType = "application/csv";

await StreamCopyOperation.CopyToAsync(CreateExportStream(models, requestLog, ct), res.Body, null, res.HttpContext.RequestAborted);

Still needs work as it looks like something is blocking the stream from completing.

from carter.

bazwilliams avatar bazwilliams commented on May 4, 2024

Looks like the response blocks because my stream has not yet been written to before passing to CopyToAsync. Hacking in a forced delay of 1 second fixes the issue, this gives time for at least the CSV headers to have been written. Not ideal. but this has got me moving forward! Thanks for the help.

from carter.

jchannon avatar jchannon commented on May 4, 2024

Just having a quick look in the depths of MVC and the FileResultExecutorBase calls:

Stream outputStream = context.Response.Body;
Stream stream = fileStream;
await StreamCopyOperation.CopyToAsync(fileStream, outputStream, new long?(), 65536, context.RequestAborted);
stream.Dispose();
stream = null;

Do you want to see if you can do what you had before the hacky timeout and see if it works

from carter.

jchannon avatar jchannon commented on May 4, 2024

OK this works:

  this.Get("/actors/download", async (request, response, routeData) =>
            {
                using (var mystream = new MemoryStream(Encoding.ASCII.GetBytes("hi")))
                {
                    var cd = new ContentDisposition { FileName = "journal.csv" };
                    response.Headers.Add("Content-Disposition", cd.ToString());
                    response.ContentType = "application/csv";
                    await StreamCopyOperation.CopyToAsync(mystream, response.Body, new long?(), 65536, request.HttpContext.RequestAborted);
                }
            });

from carter.

jchannon avatar jchannon commented on May 4, 2024

@bazwilliams you will have to create your stream first then call CopyToAsync as it will copy what's in your stream into the response body so it makes sense that it hangs if there's nothing inside it.

However that code above is ugly as hell so I think Botwin needs an extension on Response to write a memorystream to the response like the MVC FileStreamResult. Something like FromStream(Stream myStream, string contentType, ContentDisposition contentDisposition = null) and used like:

            this.Get("/actors/download", async (request, response, routeData) =>
            {
                using (var mystream = new MemoryStream(Encoding.ASCII.GetBytes("hi")))
                {
                    var cd = new ContentDisposition { FileName = "journal.csv" };
                    await response.FromStream(mystream, "application/csv", cd);
                }
            });

from carter.

jchannon avatar jchannon commented on May 4, 2024

Fancy sending a PR 🤔 👍

from carter.

bazwilliams avatar bazwilliams commented on May 4, 2024

The Nancy version of this code looks like:

var models = this.journalReader.Read(start, end, type, ct);

return Task.FromResult(new StreamResponse(() => CreateExportStream(models, requestLog, ct), "application/csv")
    .AsAttachment($"journal-{type}.csv")
    .WithStatusCode(200));

I'll see if I can solve this using pure asp.net first and report back :)

from carter.

bazwilliams avatar bazwilliams commented on May 4, 2024

Hmm - the StreamResponse() in Nancy just uses CopyTo() https://github.com/NancyFx/Nancy/blob/master/src/Nancy/Responses/StreamResponse.cs#L32

There must be something else I need to do here.

from carter.

jchannon avatar jchannon commented on May 4, 2024

from carter.

bazwilliams avatar bazwilliams commented on May 4, 2024

I'll check it out thanks. If I get it working I'll submit a PR!

from carter.

JoeStead avatar JoeStead commented on May 4, 2024

I reckon @danbarua might have some suggestions

from carter.

danbarua avatar danbarua commented on May 4, 2024

I'm using a producer/consumer pattern to minimise the memory requirements of my service - the data coming from the database could be many hundreds of MB and I'd rather stream it directly to the client.

Are there any constraints preventing you from writing out the data to file and then returning that file via the web api? That's how most reporting/CSV exports I've seen work.

from carter.

bazwilliams avatar bazwilliams commented on May 4, 2024

Technically no, but it is a service I'd like to run via lambda or something like that and just not have to worry about having an S3 bucket or local file system constraints.

It is currently deployed to AWS ECS, so I could gain access to the local file system for the duration of the request, but then I have to manage the available space and remove older files. If I were going to do this, I'd consider rewriting the whole thing and that's not something I want to do at the moment.

It's in the same solution as 5 other Ex Nancy, now Botwin projects (all working perfectly), I thought I'd try my luck at this more tricky one :)

from carter.

jchannon avatar jchannon commented on May 4, 2024

from carter.

bazwilliams avatar bazwilliams commented on May 4, 2024

DynamoDb

from carter.

bazwilliams avatar bazwilliams commented on May 4, 2024

This is definitely not a Botwin issue; I've rewritten the module as an MVC controller and I see the same problem.

Thanks for all the help so far! Looks like this is the right approach, especially after reading an article about streaming video (linked below) and the asp.net code on Github so look forward to the extension suggested above being merged.

However, I think the ProducerConsumerStream I'm using is introducing some kind of deadlock with asp.net. The way I'm using Nancy to do this seems to work, so will revert my work and park for now. I'll be considering a rewrite instead.

http://anthonygiretti.com/2018/01/16/streaming-video-asynchronously-in-asp-net-core-2-with-web-api/

Cheers!

from carter.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.