Arjun C
Arjun C

Reputation: 357

Multiple file upload in ASP.NET MVC is uploading a single file multiple times

I have a ASP.NET MVC project in which the user can upload multiple files at a time. The following code is in the View:

@using (Html.BeginForm("Edit",
            "Bacteria",
            FormMethod.Post,
            new { enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
<!--Other fields that are posted correctly to the db-->
 <div class="">
    <label class="control-label col-md-2">Attach New Files:</label>
    <div class="col-md-10">
        <input type="file" id="Attachment" name="Attachment" class="form-control" accept=".xls,.xlsx,.csv,.CSV,.png,.jpeg,.jpg,.gif,.doc,.docx,.pdf,.PDF" multiple />
    </div>
</div>
<div class="form-group">
    <div class="col-md-offset-2 col-md-10">
        <input type="submit" value="Save" class="btn btn-default" />
    </div>
</div>

The method in the controller is:

if (ModelState.IsValid)
{
    db.Entry(bacteria).State = EntityState.Modified;
    db.SaveChanges();

    foreach (string file in Request.Files)
    {
        HttpPostedFileBase hpf = Request.Files[file];
        var fileName = Path.GetFileName(hpf.FileName);
        if (fileName == null || fileName == "")
        {
            break;
        }
        var subPath = "Attachments/Bacteria/" + bacteria.ID + "/";
        bool exists = System.IO.Directory.Exists(Server.MapPath("~/" + subPath));
        if (!exists)
        {
            System.IO.Directory.CreateDirectory(Server.MapPath("~/" + subPath));
        }
        var path = Path.Combine(subPath, fileName);
        hpf.SaveAs(Server.MapPath("~/" + path));
        BacteriaAttachment a = new BacteriaAttachment()
        {
            Name = fileName,
            Bacteria = bacteria,
            Link = path
         };
         db.BacteriaAttachments.Add(a);
         db.SaveChanges();
    }
}

If I upload FileOne.png, FileTwo.png, FileThree.png; the BacteriaAttachements table will get 3 new records with all of them having the same name (such as FileOne.png), link and bacteriaID. Only their ID (the primary key) is unique. And only one file (E.g: FileOne.png) gets uploaded in the server.

So instead of the three files being uploaded, only one of them is being uploaded thrice.

Any help is much appreciated.

Thank you.

Upvotes: 0

Views: 6546

Answers (2)

dj079
dj079

Reputation: 1389

Mudassar Ahmed Khan has explained it really well here: https://www.aspsnippets.com/Articles/MVC-HttpPostedFileBase-multiple-files-Upload-multiple-files-using-HttpPostedFileBase-in-ASPNet-MVC.aspx

You should consider using List<HttpPostedFileBase> as method parameter in your controller method. And then loop through each of them. Something like below;

foreach (HttpPostedFileBase postedFile in postedFiles)
        {
            if (postedFile != null)
            {
                string fileName = Path.GetFileName(postedFile.FileName);

Hope this is helpful!

Upvotes: 0

Shyju
Shyju

Reputation: 218732

When you execute, foreach (string file in Request.Files), for each iteration, the value of file will be the string value "Attachment", which is the name of your file input. When user uploads multiple files from the same input, Request.Files stores all of them with the same key - the name of your input element, which is "Attachment". Now when you execute Request.Files["Attachment"], it will give you only the first item (because all items has same key). For all iterations of the loop, this is what happening.

When accessing Request.Files, do not use name based access approach, Use index based approach (Request.Files[zeroBasedindex]).

You can use a for loop to properly iterate through the collection and read Request.Files using index based approach.

for(var i = 0; i < Request.Files.Count; i++)
{
    HttpPostedFileBase hpf =  Request.Files[i];
    // Your existing code to save hpf.
}

I personally always use a collection of HttpPostedFileBase as my HttpPost action method parameter or as a property in my view model (which i will use a paramater as my HttpPost action method) and loop that. The important thing to remember is, your parameter/property name should match with the name of the input you are using for file upload.

public ActionResult Save(List<HttpPostedFileBase> attachment)
{
   foreach(var hpf in attachment)
   {
     // to do : save hpf
   }
   // to do : return something
}

Upvotes: 2

Related Questions