Reputation: 31643
I would like to delete a temporary file after returning it form action. How can i achieve that with ASP.NET Core:
public IActionResult Download(long id)
{
var file = "C:/temp/tempfile.zip";
var fileName = "file.zip;
return this.PhysicalFile(file, "application/zip", fileName);
// I Would like to have File.Delete(file) here !!
}
The file is too big for returning using memory stream.
Upvotes: 13
Views: 8332
Reputation: 2056
You can create a FileStream
that deletes the underlying file as soon as the stream is closed. With the option DeleteOnClose
.
As the stream is closed automatically after download, the file is deleted.
var fileStream = new FileStream(
Path.GetTempFileName(),
FileMode.Create,
FileAccess.ReadWrite,
FileShare.Read,
4096,
FileOptions.DeleteOnClose);
// add something to the file
fileStream.Position = 0;
return File(fileStream, MediaTypeNames.Application.Octet);
Upvotes: 10
Reputation: 131305
File() or PhysicalFile() return a FileResult
-derived class that just delegates processing to an executor service. PhysicalFileResult
's ExecuteResultAsync method calls :
var executor = context.HttpContext.RequestServices
.GetRequiredService<IActionResultExecutor<PhysicalFileResult>>();
return executor.ExecuteAsync(context, this);
All other FileResult-based classes work in a similar way.
The PhysicalFileResultExecutor class essentially writes the file's contents to the Response stream.
A quick and dirty solution would be to create your own PhysicalFileResult
-based class that delegates to PhysicalFileResultExecutor but deletes the file once the executor finishes :
public class TempPhysicalFileResult : PhysicalFileResult
{
public TempPhysicalFileResult(string fileName, string contentType)
: base(fileName, contentType) { }
public TempPhysicalFileResult(string fileName, MediaTypeHeaderValue contentType)
: base(fileName, contentType) { }
public override async Task ExecuteResultAsync(ActionContext context)
{
try {
await base.ExecuteResultAsync(context);
}
finally {
File.Delete(FileName);
}
}
}
Instead of calling PhysicalFile()
to create the PhysicalFileResult
you can create and return a TempPhysicalFileResult
, eg :
return new TempPhysicalFileResult(file, "application/zip"){FileDownloadName=fileName};
That's the same thing PhysicalFile() does :
[NonAction]
public virtual PhysicalFileResult PhysicalFile(
string physicalPath,
string contentType,
string fileDownloadName)
=> new PhysicalFileResult(physicalPath, contentType) { FileDownloadName = fileDownloadName };
A more sophisticated solution would be to create a custom executor that took care eg of compression as well as cleaning up files, leaving the action code clean of result formatting code
Upvotes: 19
Reputation: 1527
Since the file is big one solution is to send a token (like a guid) along side the request. Only once the download has been completed you want to delete the file (otherwise you risk losing it). So using that token the client will make a new request using the aforementioned token. So you will know that the download was successful and that you can proceed to delete the file.
I suggest using a Dictionary to map a token to a file.
Deleting on one request seems like a bad practice due to the fact that you can easily lose the file.
Also you can use Henk Mollema answer to be certain that all files are cleared periodically if that is what you are trying to achieve.
Upvotes: 1
Reputation: 1958
You can load content of file. delete file and send content in response. in MVC you can return a file in response. check the snippet below
public FileResult Download()
{
var fileName = $"client-file-name.ext";
var filepath = $"filepath";
byte[] fileBytes = System.IO.File.ReadAllBytes(filepath);
//delete file here
return File(fileBytes, "application/x-msdownload", fileName);
}
Upvotes: -1
Reputation: 46531
It would be hard to do this within the same request as the requests ends directly after the streaming is complete. A better option might be to run a timed background task via a hosted service which periodically removes all files (e.g. with a last write time of >5 minutes ago) from the temp directory.
Upvotes: 0