1

I'm currently using tinyMCE to create some news post for a website. I need to be able to upload images, however i've hit a stopblock.

When I hit my controller I get an

System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'FileBufferingReadStream'.
   at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.ThrowIfDisposed()
   at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.set_Position(Int64 value)
   at Microsoft.AspNetCore.Http.Internal.ReferenceReadStream..ctor(Stream inner, Int64 offset, Int64 length)
   at Microsoft.AspNetCore.Http.Internal.FormFile.OpenReadStream()
   at Microsoft.AspNetCore.Http.Internal.FormFile.CopyToAsync(Stream target, CancellationToken cancellationToken)
   at HardwareOnlineDk.Web.Areas.Admin.Controllers.ImageController.Upload(IFormFile inputFile) in D:\Kode\HardwareOnlineRider\HOL\SourceCode\Main\Web\Areas\Admin\Controllers\ImageController.cs:line 139

My code looks like this:

[HttpPost, ActionName("Upload")]
        public async Task<IActionResult> Upload(IFormFile inputFile)
        {
            try
            {
                var filesCount = Request.Form.Files.Count;

                if (filesCount == 0)
                    return BadRequest("Ingen fil fundet");

                // Get HTTP posted file based on the fieldname. 
                var file = Request.Form.Files.GetFile("file");

                if (file == null)
                    return BadRequest("Fejlkonfiguration: Filnavn ikke korrekt");

                // Check if the file is valid.
                if (!Check(file.FileName, file.ContentType))
                    return BadRequest("Fil ikke gyldig");

                var memoryStream = new MemoryStream();
                await file.CopyToAsync(memoryStream);
                var medie = new Medie
                {
                    Name = file.FileName.Trim('\"'),
                    ParentId = _imageService.TempFolderGuid,
                    ContentLength = file.Length,
                    Content = memoryStream.ToArray()
                };

                try
                {
                    var imageId = await _imageService.Medier_InsertMedie(medie);

                    //TODO Her skal vi gemme ImageId i Session


                    return Json(new
                    {
                        location = $"/api/media/{imageId.Id}.jpg"
                    });
                }
                catch
                {
                    return BadRequest("Kunne ikke gemme billede");
                }
            }
            catch
            {
                return StatusCode(500);
            }
        }

And the Check Method if needed

 private static bool Check(string filePath, string mimeType)
        {
            return AllowedImageExts.Contains(GetFileExtension(filePath)) &&
                   AllowedImageMimetypes.Contains(mimeType.ToLower());
        }

The code fails when i'm doing:

await file.CopyToAsync(memoryStream)

Can anyone help me here. I'm lost.

UPDATE 1 I just tried to fix it with the suggested answer, so my code now looks like this:

[HttpPost, ActionName("Upload")]
        public async Task<IActionResult> Upload([FromForm]IFormFile file)
        {
            try
            {
                if (file == null)
                    return BadRequest("Fejlkonfiguration: Filnavn ikke korrekt");

                var memoryStream = new MemoryStream();
                await file.CopyToAsync(memoryStream);
                var filesCount = Request.Form.Files.Count;
                if (!Request.ContentType.StartsWith(MultipartContentType))
                    return BadRequest("Contenttype er ikke korrekt");

                if (filesCount == 0)
                    return BadRequest("Ingen fil fundet");

                // Get HTTP posted file based on the fieldname. 

                // Check if the file is valid.
                if (!Check(file.FileName, file.ContentType))
                    return BadRequest("Fil ikke gyldig");


                var medie = new Medie
                {
                    Name = file.FileName.Trim('\"'),
                    ParentId = _imageService.TempFolderGuid,
                    ContentLength = file.Length,
                    Content = memoryStream.ToArray()
                };

                try
                {
                    var imageId = await _imageService.Medier_InsertMedie(medie);

                    //TODO Her skal vi gemme ImageId i Session


                    return Json(new
                    {
                        location = $"/api/media/{imageId.Id}.jpg"
                    });
                }
                catch
                {
                    return BadRequest("Kunne ikke gemme billede");
                }
            }
            catch
            {
                return StatusCode(500);
            }
        }

The input parameter is no longer null, but it still throws the same exception

Christian A
  • 483
  • 1
  • 10
  • 23

3 Answers3

0

You indicated that inputFile is always null.

Binding matches form files by name.

Reference Upload files in ASP.NET Core

Based on

// Get HTTP posted file based on the fieldname. 
var file = Request.Form.Files.GetFile("file");

the name of the posted field is "file"

rename the action parameter to match and explicitly state when to bind the data using [FromForm]

[HttpPost, ActionName("Upload")]
public async Task<IActionResult> Upload([FromForm]IFormFile file) {
    try {
        if (file == null)
            return BadRequest("Fejlkonfiguration: Filnavn ikke korrekt");

        // Check if the file is valid.
        if (!Check(file.Name, file.ContentType))
            return BadRequest("Fil ikke gyldig");

        var medie = new Medie {
            Name = file.Name.Trim('\"'),
            ParentId = _imageService.TempFolderGuid
        };

        var fileStream = file.OpenStreamRead();
        using (var memoryStream = new MemoryStream()) {
            await fileStream.CopyToAsync(memoryStream);
            medie.Content = memoryStream.ToArray();
            medie.ContentLength = memoryStream.Length,
        }

        try {
            var imageId = await _imageService.Medier_InsertMedie(medie);

            //TODO Her skal vi gemme ImageId i Session

            return Json(new {
                location = $"/api/media/{imageId.Id}.jpg"
            });
        } catch {
            return BadRequest("Kunne ikke gemme billede");
        }
    } catch {
        return StatusCode(500);
    }
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • I will try that and report back. Thank you for your suggestion – Christian A Jan 09 '20 at 15:31
  • I just tried your solution, and the parameter is not null anymore. It does however throw the same exception – Christian A Jan 09 '20 at 18:56
  • @ChristianA OK. To test a theory, use the non async `file.CopyTo(memoryStream)` method and see if the error persists. – Nkosi Jan 09 '20 at 18:59
  • @ChristianA also take a look and updated approach in answer. – Nkosi Jan 09 '20 at 19:14
  • I will try that. Most likely tomorrow though. Will report back – Christian A Jan 09 '20 at 21:35
  • I just figured out what it was. I have a log in middleware, that reads the httpcontext. That's what's causing the error. Adding this: if (context.Controller.ToString().Contains("ImageController")) return; fixed the issue – Christian A Jan 09 '20 at 21:42
  • @ChristianA I suspected there was something in the pipeline before it reached the controller – Nkosi Jan 09 '20 at 22:20
  • I managed to change my logging so that the HttpContext could be read twice, by using the answer here: https://stackoverflow.com/questions/58737391/how-can-i-read-http-request-body-in-netcore-3-more-than-once – Christian A Jan 09 '20 at 22:58
0

I feel like I should post how I solved it

The issue was that I have a log in middleware, that reads the httpcontext. Using the answer found here: How can I read http request body in netcore 3 more than once? solved the issue for me.

Christian A
  • 483
  • 1
  • 10
  • 23
0

you can pass additional parameters with a class with a IFormFile element. pass the byte data to a stored procure as a byte[] array. where the FileData field is a varbinary parameter in the stored procedure.

 public class FormData
    {
        public string FileName { get; set; }
        public string FileDescription { get; set; }
        public IFormFile file { get; set; }
    }

controller end point

  public async Task<IActionResult> UploadDocument([FromForm] FormData formData)
{

if (formData.file.Length > 0)
{
 using (MemoryStream ms = new MemoryStream())
 {
         await formData.file.CopyToAsync(ms);
         data = ms.ToArray();
         await _repository.spProcedure(FileData: data);
 }
                      }
Golden Lion
  • 3,840
  • 2
  • 26
  • 35