Search code examples
c#asp.net-mvcdisposeabp-framework

Delete file after returning it on an API request


I get a request with which I create a file and return it to the client.

After the file is sent I want it deleted.

Since I get many request, files are big and memory is scarce, I don't want to buffer it in memory to send it.

The only method I got to work without buffering the whole file in memory was:

Response.TransmitFile(filepath)

The problem with this is that it does it asynchronously, so if I delete it after that call the file download is interrupted.

I tried calling Flush, adding the delete on a finally block but neither of those worked. I thought of inheriting HttpResponse to try modify TransmitFile, but it's a sealed class. I tried to use HttpResponse.ClientDisconnectedToken but either I don't understand how to use it correctly or it isn't working in this case.

How can I achieve this? Is there a better method than calling HttpResponse's TransmitFile? Always taking into account that this is an API, files can't be broken into different requests and that it doesn't load the full file in memory.

I'm not sure if it could help somehow, but my controller is inheriting from AbpApiController.


Solution

  • An implementation of TransmitFileWithLimit, It can be improve in many ways, but it works

    Extension for HttpResponse

    public static class HttpResponseExtensions
    {
        public static void TransmitFileWithLimit(this HttpResponse response, string path, int limit)
        {
            var buffer = new byte[limit];
            var offset = 0;
            response.ClearContent();
            response.Clear();
            response.ContentType = "text/plain";
    
            using (var fileStream = File.OpenRead(path))
            {
                var lengthStream = fileStream.Length;
                while (offset < lengthStream)
                {
                    var lengthBytes = fileStream.Read(buffer, 0, limit);
                    var chars = System.Text.Encoding.ASCII.GetString(buffer, 0, lengthBytes).ToCharArray();
                    response.Write(chars, 0, lengthBytes);
                    offset += lengthBytes;
                }
            }
            response.Flush();
            response.End();
        }
    }
    

    Inside Controller

        public void Get()
        {
            var path = @"C:\temporal\bigfile.mp4";
            var response = HttpContext.Current.Response;
            response.ClearContent();
            response.Clear();
            response.AddHeader("Content-Disposition", "inline; filename=" + HttpUtility.UrlPathEncode(path));
            response.ContentType = "text/plain";
            response.AddHeader("Content-Length", new FileInfo(path).Length.ToString());
            response.Flush();
            HttpContext.Current.ApplicationInstance.CompleteRequest();
    
            TransmitFileWithLimit(path, 10000);
            File.Delete(path);
        }