Handling FileContentResult when file is not found
Asked Answered
R

3

11

I have a controller action that downloads a file from an azure blob based on the container reference name (i.e. full path name of the file in the blob). The code looks something like this:

public FileContentResult GetDocument(String pathName)
{
    try
    {
        Byte[] buffer = BlobStorage.DownloadFile(pathName);
        FileContentResult result = new FileContentResult(buffer, "PDF");
        String[] folders = pathName.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
        // get the last one as actual "file name" based on some convention
        result.FileDownloadName = folders[folders.Length - 1];

        return result;
    }
    catch (Exception ex)
    {
        // log error
    }
    // how to handle if file is not found?
    return new FileContentResult(new byte[] { }, "PDF");
}

The BlobStorage class there is my helper class to download the stream from the blob.

My question is stated in the code comment: How should I handle the scenario when the file/stream is not found? Currently, I am passing an empty PDF file, which I feel is not the best way to do it.

Rafferty answered 23/6, 2011 at 7:7 Comment(5)
@Stecya: This is called from the view via javascript.Rafferty
What should be the datatype on the ajax call? @Alex R could you also show how the ajax call is made in the view it would be helpful.Nonobjective
The question is already answered. Are you also having the same issue? The call from the javascript is simply window.location.href = "GetDocument?pathName=" + docPath;. That will call the controller action above and respond accordingly.Rafferty
yes i have , i am not able open the file my view looks like this var options = { iframe: true, dataType: "html", url: "Upload/Previewfile" }; preview file is similar to your GetDocument after executing this ishould get popup to open file but i am not getting.Do you have any idea or shall i post as a question? thanks.Nonobjective
Yeah, better post it as a separate question. That's how it works here. I'll be glad to help if I can.Rafferty
S
22

The correct way to handle a not found in a web application is by returning a 404 HTTP status code to the client which in ASP.NET MVC terms translates into returning a HttpNotFoundResult from your controller action:

return new HttpNotFoundResult();

Ahh, oops, didn't notice you were still on ASP.NET MVC 2. You could implement it yourself because HttpNotFoundResult was introduced only in ASP.NET MVC 3:

public class HttpNotFoundResult : ActionResult
{
    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        context.HttpContext.Response.StatusCode = 404;
    }
}
Scrawly answered 23/6, 2011 at 7:9 Comment(4)
In case of MVC3, is HttpNotFoundResult inherited from ActionResult? In this case the definition of the action method should be public ActionResult GetDocument(String pathName), Right?Opium
@LordCover, right. I would recommend you to always use ActionResult as return type in your action signatures. I have also updated my answer to provide a sample implementation of HttpNotFoundResult.Scrawly
Well thanks, usually I use the higher level in such cases as this question, but if I have one and only one definite value like JsonResult I use it alone. Yes I noticed it and +1.Opium
Thanks, unfortunately I am on MVC2 still. (Will need to upgrade soon!) I'll implement what you said and see.Rafferty
O
6

In ASP.NET Core, use NotFound()

Your controller must inherit of Controller and the method must return ActionResult

Example:

public ActionResult GetFile(string path)
{
    if (!File.Exists(path))
    {
        return NotFound();
    }
    
    try
    {
        return new FileContentResult(File.ReadAllBytes(path), "application/octet-stream");
    }
    catch (FileNotFoundException)
    {
        return NotFound();
    }
}

Note: the above code doesn't handle all cases of file operations errors as invalid chars in path or file unavailability (because it is beyond the scope of the current answer) and proper responses (like restricted file access due to permissions or else)

Obvious answered 11/1, 2019 at 2:56 Comment(4)
actually, for me that didn't work, what worked for me was : return new NotFoundResult();Boomkin
Also checking File.Exists() is bad practice (github.com/dotnet/dotnet-api-docs/issues/3277) you should rather catch the FileNotFoundException of ReadAllBytes()Cytochemistry
@DanielHabenicht Using File.Exists() is not a bad practice. It's assuming that file will exist in all cases after the check that is a bad practice. In most of the use cases, calling File.Exists will avoid throwing an exception when the file doesn't exist.Obvious
Yes, but if your sole use case is to serve that file. Throwing and catching the error instead of checking before is more clear than writing duplicate code.Cytochemistry
W
0

Using the CQRS pattern in ASP.NET Core, you can return NoContentResult.

public override async Task<ActionResult> Handle(GetProfileImageQuery request, CancellationToken cancellationToken)
{
    var item = await _mediaProfileImageRepository.GetByUserIdAsNoTracking(request.UserId, cancellationToken);
    if (item is null)
    {
        return new NoContentResult();
    }
    var fileContents = await File.ReadAllBytesAsync(item.FilePath, cancellationToken);

    return new FileContentResult(fileContents, item.ContentType)
    {
        LastModified = item.ModifiedAt,
        FileDownloadName = item.OriginalFileName
    };
}
Wenoa answered 29/5 at 14:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.