I am following How-to-submit-additional-form-data and have a working fileupload that is submitting the additional form data, ie. the additional textarea input field, description[]
, with chunking and multiple file support without setting formData:{ }
on .fileupload()
. My problem is that I am only able to submit the field as an array, description[]
, and I am unable to come up with some logic to associate the appropriate description[i]
with the appropriate file. I am using asp.net mvc which should not matter in this case.
The View:
<script type="text/javascript">
$(document).ready(function () {
$('#btnContinue').prop('disabled', true);
$('#fileupload').fileupload({
url: '@Url.Action("UploadChunk", "Upload")',
maxChunkSize: 1048576,
}).addClass('fileupload-processing')
.bind('fileuploadalways', function (e, data) {
if ($('#fileIds').val().indexOf(';' + data.result.id + ';') == -1) {
var pre = $('#fileIds').val() == "" ? ';' : '';
var append = $('#fileIds').val() + pre + data.result.id + ';';
$('#fileIds').val(append);
}
})
.bind('fileuploaddone', function (e, data) {
$('#btnContinue').prop('disabled', false);
});
$.ajax({
url: $('#fileupload').fileupload('option', 'url'),
dataType: 'json',
context: $('#fileupload')[0]
}).always(function () {
$(this).removeClass('fileupload-processing');
});
});
</script>
@using (Html.BeginForm("FileSet", "Upload", FormMethod.Post, new { id = "fileupload" } ))
{
@Html.AntiForgeryToken()
@Html.Hidden("fileIds", "", new { @id = "fileIds" })
<div class="form-horizontal">
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="row fileupload-buttonbar">
<div class="col-lg-7">
<!-- The fileinput-button span is used to style the file input field as button -->
<span class="btn btn-success fileinput-button">
<i class="glyphicon glyphicon-plus"></i>
<span>Add files...</span>
<input type="file" name="files[]" multiple>
</span>
<button type="submit" class="btn btn-primary start" id="btnStartUpload">
<i class="glyphicon glyphicon-upload"></i>
<span>Start upload</span>
</button>
<button type="reset" class="btn btn-warning cancel" id="btnCancelUpload">
<i class="glyphicon glyphicon-ban-circle"></i>
<span>Cancel upload</span>
</button>
<button type="button" class="btn btn-danger delete" id="btnDeleteUpload">
<i class="glyphicon glyphicon-trash"></i>
<span>Delete</span>
</button>
<input style="visibility: hidden" type="checkbox" class="toggle">
<!-- The global file processing state -->
<span class="fileupload-process"></span>
</div>
<!-- The global progress state -->
<div class="col-lg-5 fileupload-progress fade">
<!-- The global progress bar -->
<div class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100">
<div class="progress-bar progress-bar-success" style="width:0%;"></div>
</div>
<!-- The extended global progress state -->
<div class="progress-extended"> </div>
</div>
</div>
<table role="presentation" class="table table-striped"><tbody class="files"></tbody></table>
<div class="row">
<div class="col-md-10">
<button type="submit" id="btnContinue" class="btn btn-primary">Continue</button>
</div>
</div>
</div>
}
<!-- The template to display files available for upload -->
<script id="template-upload" type="text/x-tmpl">
{% for (var i=0, file; file=o.files[i]; i++) { %}
<tr class="template-upload fade">
<td>
<span class="preview"></span>
</td>
<td>
<p class="name">{%=file.name%}</p>
<strong class="error text-danger"></strong>
</td>
<td>
<input type="hidden" name="locator[]" value="{%=file.name%}" />
<textarea name="description[]" placeholder="Enter description" class="form-control" rows="3"></textarea>
<strong class="error text-danger"></strong>
</td>
<td>
<p class="size">Processing...</p>
<div class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><div class="progress-bar progress-bar-success" style="width:0%;"></div></div>
</td>
<td>
{% if (!i && !o.options.autoUpload) { %}
<button class="btn btn-primary start" disabled>
<i class="glyphicon glyphicon-upload"></i>
<span>Start</span>
</button>
{% } %}
{% if (!i) { %}
<button class="btn btn-warning cancel">
<i class="glyphicon glyphicon-ban-circle"></i>
<span>Cancel</span>
</button>
{% } %}
</td>
</tr>
{% } %}
</script>
<!-- The template to display files available for download -->
<script id="template-download" type="text/x-tmpl">
{% for (var i=0, file; file=o.files[i]; i++) { %}
<tr class="template-download fade">
<td>
<span class="preview">
{% if (file.thumbnailUrl) { %}
<a href="{%=file.url%}" title="{%=file.name%}" download="{%=file.name%}" data-gallery><img src="{%=file.thumbnailUrl%}"></a>
{% } %}
</span>
</td>
<td>
<p class="name">
{% if (file.url) { %}
<a href="{%=file.url%}" title="{%=file.name%}" download="{%=file.name%}" {%=file.thumbnailUrl?'data-gallery':''%}>{%=file.name%}</a>
{% } else { %}
<span>{%=file.name%}</span>
{% } %}
</p>
{% if (file.error) { %}
<div><span class="label label-danger">Error</span> {%=file.error%}</div>
{% } %}
</td>
<td>
<span class="size">{%=o.formatFileSize(file.size)%}</span>
</td>
<td>
{% if (file.deleteUrl) { %}
<button class="btn btn-danger delete" data-type="{%=file.deleteType%}" data-url="{%=file.deleteUrl%}" {% if (file.deletewithcredentials) { %} data-xhr-fields='{"withCredentials":true}' {% } %}>
<i class="glyphicon glyphicon-trash"></i>
<span>Delete</span>
</button>
<input type="checkbox" name="delete" value="1" class="toggle">
{% } else { %}
<button class="btn btn-warning cancel">
<i class="glyphicon glyphicon-ban-circle"></i>
<span>Cancel</span>
</button>
{% } %}
</td>
</tr>
{% } %}
The Controller:
[HttpPost]
public ContentResult UploadChunk(string[] description)
{
ParseRequestHeaderRanges(this.Request);
var r = new List<UploadFilesResult>();
var i = 0;
foreach (string file in Request.Files)
{
HttpPostedFileBase hpf = Request.Files[file] as HttpPostedFileBase;
if (hpf.ContentLength == 0)
continue;
var uploader = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
var result = DataTranslator.UploadChunk(hpf.InputStream, hpf.FileName, description[i], hpf.ContentType,
hpf.ContentLength, (int)hpf.InputStream.Length, this.TotalBytes, uploader);
r.Add(result);
i++;
}
return Content("{\"id\":\"" + r[0].Id + "\",\"name\":\"" + r[0].Name + "\",\"type\":\"" + r[0].Type + "\",\"size\":\"" + string.Format("{0} bytes", r[0].Length) + "\"}", "application/json");
}
Rather than have it be an array make it a hash? Say using the filename? That way you don't have to rely on things being in the right order.
And in the controller: