I've got the simplest upload control with batch mode enabled.
View is;
@(Html.Kendo().Upload().Name("uploadFiles").Async(a => a.Save("Upload", "Home").AutoUpload(false).Batch(true)))
Controller is;
public JsonResult Upload(IEnumerable<HttpPostedFileBase> uploadFiles)
{
return new JsonResult();
}
Hoping to see a single call to the action which handles the upload with multiple files. Instead, once I hit upload, I see a separate request for each file that was selected. I must be doing something wrong but finding it hard to figure out what, since the setup is so simple.
Running it in IIS Express, however I've tried with IIS8 and that didn't work either.
Telerik Version: 2014.2.1008
Browser : Chrome 37.0.2062.124
Thanks a lot.
Chris R
15 Answers, 1 is accepted
This behavior is expected. The batch mode applies to multiple files, which are selected at the same time i.e. a single list item and a single input are created for them. Files selected one after the other, as in the current case will be uploaded in separate requests.
Let me know if this information helps or I could assist further.
Regards,
Dimiter Madjarov
Telerik
Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.
Thanks a lot for the response. Whilst fully understanding the way the control behaves as of now, may I pick your brains a bit more.
As you can see in the attached image, I want the user to upload multiple files with each having its own copy of meta data (A start & end date in this instance). It's essential that the files and their meta data reach the server in 1 request for the file processing and validations. Do you see me achieving this anyway in async mode without going for a form submit?
btw I'm open for hacking the kendo js if we want to go that far.
You could use the following approach to attach the metadata. Each file have unique uid property which is set as data-uid attribute to the list item. You could use them to access the associated date pickers.
E.g.
.Events(e => e.Upload(
"onUpload"
))
function
onUpload(e) {
var
uid = e.files[0].uid;
var
listItem =
this
.wrapper.find(
".k-file[data-uid='"
+ uid +
"']"
);
//use listItem to access the date pickers and attach metadata
}
Regards,
Dimiter Madjarov
Telerik
Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.
Thank you for your continuous help. I may have misguided the conversation by mentioning meta data - as I have no problem attaching it to the request. The problem is forcing the control to send multiple files (which were added as separate list items) in 1 request in async mode.
As you correctly pointed out this is not available by default through configuration. What I'm after now, is to get this working by either modifying kendo code or any other 'javascripty' way. Any guidance is much appreciated.
Regards,
IM
At the moment this is not possible with the Kendo UI Upload widget, but you could post it as a suggestion in our Feedback portal. Regarding the custom modifications of the Kendo UI source code, such are not supported, so I could not provide technical assistance in that matter.
Regards,
Dimiter Madjarov
Telerik
Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.
Thank you for posting the suggestion. If it's popular among the community and needed from other users too, we will consider to implement it in future versions of Kendo UI.
Regards,
Dimiter Madjarov
Telerik
Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.
is it possible if using non-ajax? (say with form post) > is this the best only alternative?
I'll update this thread for community reference, as we've already addressed the issue in a support ticket.
The recommended approach to associate the individual uploads is to transmit the originating entity ID as metadata. Files can be stored in a temporary location until the create/update operation is complete.
Regards,
T. Tsonev
Telerik
Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.
Hello David,
Does the built in async.batch option suits the current requirements? It will allow uploading all files selected at once in a single request.
Regards,Dimiter Madjarov
Telerik by Progress
It worked fine if you either only selected one file or if you selected multiple files at once upon clicking the "Select Files" button, however, if you selected one file, then clicked "Selected Files" button again to pick a different file or files, the behavior was that the action wired up to Save gets called more than once as David Yardy mentioned. Here's how I made it work for my use case. What I wanted was to be able to have the uploader control as part of another form where the user was entering other data as well as optionally attaching files. I made use of ASP MVC Application cache to save the documents temporarily that then can be grabbed out of memory by a subsequent server request to save all of the form data including the attachments if there were any.
@using System.Web.UI.HtmlControls
@model MvcTemplate.UploadTestVm
@{
ViewBag.Title = "Index";
}
<
script
language
=
"javascript"
>
function getViewModelFromFormData() {
//Return Anonymous Javascript object with same properties as your
//view model object with values from the form controls
var returnObj = {
Name: $('#txtName').val()
};
return returnObj;
}
function saveData() {
var ctrlKendoUploader = $('#updFiles').data('kendoUpload');
var filesUploaded = ctrlKendoUploader.getFiles();
if (filesUploaded.length > 0) {
//this will cause the upload control queue up event to post the files using the specified save action on the upload control
ctrlKendoUploader.upload();
} else {
//If the user did not attached files then just get the other form data and call method to post form
var vmObj = getViewModelFromFormData();
submitForm(vmObj);
}
}
function submitForm(formDataVm) {
//Ajax call to post for the form data
var url = '@Url.Action("SaveFormData", "Upload", new { fileUploaderId = ViewBag.FileUploaderId })';
$.ajax(
{
url: url,
type: "POST",
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(formDataVm),
success: function (data) {
var ctrlKendoUploader = $('#updFiles').data('kendoUpload');
if (data.succesInd === true) {
alert('Success!');
} else {
ctrlKendoUploader.clearAllFiles();
alert('Failure!');
}
}
});
}
function onUploadComplete(e) {
//On upload complete then get a JSON object
//representing your View Model and call method to post via AJAX
var vmObj = getViewModelFromFormData();
submitForm(vmObj);
}
function onUploadSelect(e) {
setTimeout(function () {
//hide the 'Upload' and 'Cancel' buttons
$(".k-clear-selected").hide();
$(".k-upload-selected").hide();
}, 10);
}
function onUploadError(e) {
debugger;
if (e.operation == 'upload') {
alert('error');
//do some other error handling
}
}
</
script
>
<
h2
>Upload Test Page</
h2
>
<
div
class
=
"row"
>
@(Html.Kendo().Upload()
.Name( "updFiles" )
.Multiple( true )
.Async( pA => pA
.AutoUpload( false )
.Batch( true )
.Save( "ProcessFiles", "Upload" , new {fileUploaderId = ViewBag.FileUploaderId })
)
.ShowFileList( true )
.Events( pE => pE
.Select( "onUploadSelect" )
.Complete( "onUploadComplete" )
.Error("onUploadError")
)
)
</
div
>
<
div
class
=
"row"
>
<
div
class
=
"form-group"
>
<
label
class
=
"control-label"
for
=
"txtName"
>Name:</
label
>
@Html.TextBoxFor(m => m.Name, new { id="txtName", @class = "form-control input-sm", placeholder = "Enter a name" })
</
div
>
</
div
>
<
div
class
=
"row"
>
<
button
class
=
"btn btn-primary"
onclick
=
"saveData();"
>Save</
button
>
</
div
>
Controller Code
public
ActionResult Index()
{
var uploadVm =
new
UploadTestVm {Name=
""
};
ViewBag.FileUploaderId = Guid.NewGuid();
return
View( uploadVm );
}
public
ActionResult ProcessFiles( IEnumerable<HttpPostedFileBase> updFiles,
string
fileUploaderId)
{
List<HttpPostedFileBase> fileUploads =
new
List<HttpPostedFileBase>();
//Use lock to make the collection thread-safe
lock
(fileUploads)
{
if
(HttpContext.Application[fileUploaderId] ==
null
)
{
HttpContext.Application[fileUploaderId] = fileUploads;
}
else
{
fileUploads = (List<HttpPostedFileBase>)HttpContext.Application[fileUploaderId];
}
fileUploads.AddRange(updFiles);
}
return
Json(
new
{successInd =
true
, numDocsAttached = updFiles.Count()
/*documentList.Count*/
} );
}
public
ActionResult SaveFormData(UploadTestVm uploadTest,
string
fileUploaderId)
{
var success =
true
;
var returnMsg =
""
;
try
{
var fileList = HttpContext.Application[fileUploaderId]
as
List<HttpPostedFileBase>;
//Call your code to save both the form data and the files
}
catch
( Exception ex )
{
success =
false
;
returnMsg = ex.Message;
}
finally
{
HttpContext.Application.Remove(fileUploaderId);
}
return
Json(
new
{succesInd = success, msg = returnMsg } );
}
The one caveat being that you have to configure in web.config the <httpRuntime> tag under <system.web> to allow for attachments larger than 80 K in one request.
<
httpRuntime
maxRequestLength
=
"20480"
requestLengthDiskThreshold
=
"4096"
/>
Hello David,
Thank you for sharing the approach with the community. Indeed files selected by clicking the "Select files" button a second time will be uploaded in another request. This behavior is by design.
Regards,Dimiter Madjarov
Telerik by Progress
Batch mode complete solution is here: https://www.telerik.com/forums/how-to-set-initial-files-from-model-when-using-upload-tag-helper#KWe1kZJ-aEig9eoe1Q7_lA