自分の環境(Visual Studio Community 2015, .NET 4.6.1, MVC5)で検証してみました。
フォルダが異なる複数のファイルを一度にアップするのは普通のやり方(form を submit する方法。下のサンプルコードで言うと[Upload by Submit]ボタンをクリック)ではどうしてもダメでした。
nt4c さんがレスに書かれたようにブラウザの HTML5 File API と FormData を利用し Ajax で送信するのがよさそうです。
以下に検証に使ったコードを書いておきます。説明はコメントにありますのでそれを見てください。手抜きでスミマセン。不明点があれば質問してください。
Model
public class MultipleUploadModels
{
public string CustomField { get; set; }
public IList<HttpPostedFileBase> PostedFiles { get; set; }
}
View
@model Mvc5App.Controllers.MultipleUploadModels
@{
ViewBag.Title = "MultipleUpload";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>MultipleUpload</h2>
@using (Html.BeginForm("MultipleUpload", "Home", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
// name 属性はモデルのクラスのプロパティ名と同じにしない
// とサーバー側でモデルバインディングされないので注意。
// 大文字小文字は区別しない。
<input id="mutiplefileupload" type="file" name="postedfiles" multiple="multiple" />
<button type="submit">Upload by Submit</button>
<br />
@Html.Raw(ViewBag.Result)
}
<br />
<input type="button" id="ajaxUpload" value="Ajax Upload" />
<br />
<div id="result"></div>
@section Scripts {
<script type="text/javascript">
//<![CDATA[
// FormData オブジェクトの利用
// https://developer.mozilla.org/ja/docs/Web/Guide/Using_FormData_Objects
// ブラウザの HTML5 File API サポートを確認
if (window.File && window.FileReader && window.FileList) {
// FormData オブジェクトの利用
// CSRF 用のトークンを取得するため form から FromData を取得
var fd = new FormData(document.querySelector("form"));
// input type=file" でファイルの選択が完了すると change
// イベントが発生するのでそれにリスナをアタッチし、そこ
// で FileList オブジェクトを取得して選択した各ファイル
//(File オブジェクト)を FormData に追加
var fileUpload = document.getElementById("mutiplefileupload");
fileUpload.addEventListener('change', function (e) {
// files プロパティで FileList オブジェクトを取得
var filelist = fileUpload.files;
for (let i = 0; i < filelist.length; i++) {
// File オブジェクトを FormData に追加
// 名前 "postedfiles" はモデルのプロパティ名と同じにする
fd.append("postedfiles", filelist[i]);
}
});
// [Ajax Upload] ボタンクリックの処置
$('#ajaxUpload').on('click', function (e) {
$.ajax({
url: '/home/multipleupload',
method: 'post',
data: fd,
processData: false, // jQuery にデータを処理させない
contentType: false // contentType を設定させない
}).done(function (response) {
$("#result").empty;
$("#result").html(response);
}).fail(function (jqXHR, textStatus, errorThrown) {
$("#result").empty;
$("#result").text('textStatus: ' + textStatus +
', errorThrown: ' + errorThrown);
});
});
} else {
$("#result").empty;
$("#result").text('File API がサポートされてません。');
}
//]]>
</script>
}
Controller / Action Method
public ActionResult MultipleUpload()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult MultipleUpload(MultipleUploadModels model)
{
string result = "";
IList<HttpPostedFileBase> postedFiles = model.PostedFiles;
if (postedFiles != null && postedFiles.Count > 0)
{
foreach (HttpPostedFileBase postedFile in postedFiles)
{
if (postedFile != null && postedFile.ContentLength > 0)
{
// アップロードされたファイル名を取得。ブラウザが IE の
// 場合 postedFile.FileName はクライアント側でのフル
// パスになることがあるので Path.GetFileName を使う
string filename = Path.GetFileName(postedFile.FileName);
// 保存ホルダの物理パス\ファイル名
string path = Server.MapPath("~/UploadedFiles") + "\" + filename;
// アップロードされたファイルを保存
postedFile.SaveAs(path);
result += filename + " (" + postedFile.ContentType + ") - " +
postedFile.ContentLength.ToString() +
" bytes アップロード完了<br />";
}
}
}
else
{
result = "ファイルアップロードに失敗しました";
}
if (Request.IsAjaxRequest())
{
return Content(result);
}
else
{
ViewBag.Result = result;
return View();
}
}
実行結果は以下の画像のようになります。使ったブラウザは Chrome 76.0.3809.132 です。
ファイルの選択
これより先に Migration フォルダの画像を一つ選択しています。

ファイルアップロード完了後の応答画面
[Ajax Upload]ボタンをクリックして Ajax でファイルを 3 つ送信し、戻ってきた応答を表示したものです。

アップロードしたファイル
期待通りサーバー側の指定フォルダに保存されています。
