i'm using Dropzonejs and what I would like to do is essentially simple. But I can't find any article about it.
So on sending the file we trigger an error on nasty .exe and .php files. The dropzonejs interface show an X and and error message. So that's correct. The problem is that it still gets thrown into the on success event, and gets uploaded.
uploader.on("sending", function (file, xhr, data) {
var aDeny = [ // deny file some extensions by default
'application/x-msdownload',
'text/php'
];
if($.inArray(file.type, aDeny) !== -1) {
this.defaultOptions.error(file, 'File not allowed: ' + file.name);
return false;
}
});
Evil.exe still appears in this success event and gets uploaded. The response only has a string of the file path and file.status is success.
uploader.on('success', function (file, response) {
getData({'dostuff'});
return file.previewElement.classList.add("dz-success");
});
So in my 'sending' event, how can I prevent the file from appearing in the success event?
UPDATE:
Thanks! This is what I needed in the end:
var aDeny = [ // deny file some extensions by default
'application/x-msdownload',
'text/php'
];
Dropzone.options.uploadWidget = {
// more config...
accept: function (file, done) {
if ($.inArray(file.type, aDeny) !== -1) {
done("This file is not accepted!");
}
else {
done();
}
}
}
I would first of all always check the file type on the server side to prevent any problems.
Then to filter file types with Dropzone you can use:
acceptedFiles option
The default implementation of accept checks the file's mime type or
extension against this list. This is a comma separated list of mime
types or file extensions.
Eg.: image/*,application/pdf,.psd
If the Dropzone is clickable this option will also be used as accept
parameter on the hidden file input as well.
Sample:
var myDropzone = new Dropzone("div#myId", {
url: "/file/post",
acceptedFiles: 'application/x-msdownload,text/php'
});
accept function
A function that gets a file and a done function as parameters.
If the done function is invoked without arguments, the file is
"accepted" and will be processed. If you pass an error message, the
file is rejected, and the error message will be displayed. This
function will not be called if the file is too big or doesn't match
the mime types.
Sample:
Dropzone.options.myAwesomeDropzone = {
paramName: "file", // The name that will be used to transfer the file
maxFilesize: 2, // MB
accept: function(file, done) {
if (file.name == "justinbieber.jpg") {
done("This file is not accepted!");
}
else { done(); }
}
};
Related
I am new to Promises and I am sure I am doing something wrong with my code.
The result is correct but I have an Uncaught (in promise) warning in my console.log.
What is going on here is the User can submit a form where there are some required fields, some optional fields and an image.
On submit I am getting the image that gets compressed, resized and oriented so that it upload to the server as a small amount of Kbs.
In the coontroller I validate the code as said, some fields are required so in the error case inside the Ajax call I get the textStatus if a field is missing.
What happens with this code is that if the user inputs the image but none or some of the required fields the XHR textstatus error object appears as Uncaught (in promise), (The missing required fields).
I am missing the point of how you deal with errors (reject ? ) in promises so I don't really know how to solve this one.
My guess is that if the User submits the image but not the required fields there should be a way to check that during the promise so that it gets rejected even if the user submitted the image (as the image alone does not suffice). Is this done with reject ? But where? and how do you call the error after .then() ?
And what if the user submits some required fields or all of them but not the image? If I let the promise run I get an undefined error in the promise, that's why I added a check to see if there's any file and if it's an image.
This is my script:
$(document).ready(function () {
$("#AddModel").on("submit", function (e) {
e.preventDefault();
// gets the file from the form input
var files = $('#modelpic').prop('files');
var form = $(this);
// this strips the submitted form from the file and returns a new form with all the
// inputs but the file
var processedFormData = noFileFormData(form[0]);
// only if there's a file and it's an image
if( files.length !== 0 && /^image/.test(files[0].type)){
//this calls the promise that manipulates the image and returns a blob
processImage(files[0]).then(([img, ia])=> {
processedFormData.append('image', ia, 'processed.jpg');
return $.ajax({
type: 'POST',
url: form.prop('action'),
processData: false,
contentType: false,
cache: false,
data: processedFormData,
success: function (data) {
//displays preview of the post
},
error: function (textStatus) {
//displays errors
}
});
});
}else{
//displays an error re the image is not present.
// Now this is not optimal as the image is not the only required field
}
});
});
This is the promise function that prepares the manipulated image, it calls some other functions for the real processing:
function processImage(file) {
return new Promise(function (resolve, reject) {
if (file.type.match('image.*') && file.length != 0) {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = function () {
var base64img = this.result;
var exif = EXIF.readFromBinaryFile(base64ToArrayBuffer(this.result));
var srcOrientation = exif.Orientation;
resetOrientationResizeCompress(base64img, srcOrientation).then((img)=> {
dataURItoBlob(img).then((ia)=> {
resolve([img, ia]);
});
});
};
}else{
//don't really know what to do here really, or if this is the way
reject();
}
});
}
It is telling you that you are not catching the error rejection, append .catch to the processImage(files[0]) promise.
I'm using Ajax for a fileupload app that list the uploaded files without refreshing the page, the user can then download all the files uploaded as .zip.
The problem is that I'd like to send (POST method) an array with all the files path to my backend so they can be compressed as .zip and I have some issues storing them all the paths into one single array like this : array = ['path1', 'path2', 'path3']
Since the files are uploaded one by one here is what happens to my array on console.log() :
array = ['path1']
array = ['path2']
array = ['path3']
...
The backend expect all the files to be sent directly in one array in order to group them in a .zip file. How can I do it ?
Here's the ajax upload code :
$("#fileupload").fileupload({
dataType: 'json',
sequentialUploads: true, /* SEND THE FILES ONE BY ONE */
done: function (e, data) {
if (data.result.is_valid) {
mylist = [];
mylist.push(data.result.url);
console.log(mylist); /* HERE IS THE CONSOLE.LOG() */
$(document).on('submit', '#formDownload', function(e){
e.preventDefault();
if($(this).is('#formDownload')) {
$.ajax({
type:'POST',
url:'/tools/getfiles/',
data:{
path:mylist, /* array that'll be received by the backend*/
csrfmiddlewaretoken:$('#formDownload input[name=csrfmiddlewaretoken]').val(),
},
});
}
});
}
}
});
Is there any way to resolve this ?
I am using Dropzone.js to upload an Excel file, of which it's contents is then imported to a table in my database.
I currently have methods in my c# which check the file being uploaded, to make sure it is valid (checks header row) and can be imported to the database.
The validation works fine, as does DropZone.js in theory. However, no matter if the file passes validation and is imported, or not, DropZone will always show the 'tick/check' mark - to notify the user that the action has completed successfully.
Here is my Dropzone:
Dropzone.options.forecastDropzone = {
init: function () {
thisDropzone = this;
this.on("success", function (file, Message) {
console.log(Message.Message)
toastr.info(Message.Message, {
timeOut: 0, tapToDismiss: true, preventDuplicates: true
});
});
},
};
HTML:
<form action="/Power/Upload" class="dropzone" id="forecastDropzone"></form>
And the 'Upload' method which is being called:
[HttpPost]
public ActionResult Upload()
{
string filename = "";
string path = "";
try
{
foreach (string fileName in Request.Files)
{
HttpPostedFileBase file = Request.Files[fileName];
path = AppDomain.CurrentDomain.BaseDirectory + "Uploads/";
filename = Path.GetFileName(Request.Files[fileName].FileName);
Request.Files[fileName].SaveAs(Path.Combine(path, filename));
ValidateExcel(path, filename);
}
}
catch
{
isSavedSuccessfully = false;
}
return Json(isSavedSuccessfully == true ? new { Message = "Successfully Saved!" } : new { Message = "Error in saving file" });
}
So the Upload method is returning a JSON object. And I want DropZone to determine whether the save/import was successful, based on a value from the JSON. Is this possible?
Many thanks
Instead of trying to parse the JSON response and handle the error client side, I would make your server responsible for this.
Specifically: have your server return something other than a successful HTTP 200 response when an upload fails. DropZone will treat an upload as failed if it receives a 4xx or 5xx response from the server.
I'm stuck at the moment trying to figure out how can I send the antiforgery token using Dropzone.js and vanilla javascript (no jQuery).
This is my initialization code at the moment:
$(document).ready(function (e) {
var myDropzone = new Dropzone("#myDropzone", { url: "/Media/AjaxUpload", maxFilesize: 10, addRemoveLinks: true, maxFiles: 1 });
myDropzone.on("success", function (response) {
//Do some personal stuff.
});
myDropzone.on("sending", function (xhr, formData) {
formData["__RequestAntiForgeryToken"] = document.getElementsByName("__RequestVerificationToken")[1].value
});
});
I have tried appending the token to no avail on Dropzone's sending event, even at the header. Any suggestions on how to achieve this?
I think you nearly nailed it there on the first go, Jose. The small mistake you made was missing an argument in the event handler function.
From the Dropzone documentation:
Called just before each file is sent. Gets the xhr object and the
formData objects as second and third parameters, so you can modify
them (for example to add a CSRF token) or add additional data.
The event arguments are actually file, xhr, formData, and if you include all three then you can manipulate the form successfully. The advantage of doing things this way is that there is no need to create a custom attribute, just use the ValidateAntiForgeryToken attribute.
myDropzone.on("sending", function (file, xhr, formData) {
formData["__RequestAntiForgeryToken"] = document.getElementsByName("__RequestVerificationToken")[1].value;
});
I have tested this with a slightly different implemetation than your's, and it works nicely.
The way I ended up achieving this was through many suggestions at Stackoverflow. I created a special filter on MVC and passed the token via the headers. Like this:
Taking the idea from here:
http://johan.driessen.se/posts/Updated-Anti-XSRF-Validation-for-ASP.NET-MVC-4-RC
I managed to send the token via dropzone's header:
The code ended up like this:
var myDropzone = new Dropzone("#myDropzone", {
url: "/Media/AjaxUpload", maxFilesize: 10, addRemoveLinks: true, maxFiles: 1,
headers: { "__RequestVerificationToken": document.getElementsByName("__RequestVerificationToken")[1].value }
});
I added "headers" to the Dropzone instantiation, and added the filter to MVC:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple= false, Inherited = false)]
public sealed class ValidateJsonAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
var httpContext = filterContext.HttpContext;
var cookie = httpContext.Request.Cookies[AntiForgeryConfig.CookieName];
AntiForgery.Validate(cookie != null ? cookie.Value : null,
httpContext.Request.Headers["__RequestVerificationToken"]);
}
}
Then apply to your controller:
[ValidateJsonAntiForgeryToken]
public JsonResult AjaxUpload(HttpPostedFileBase file)
{
//Do Logic here!
return Json("Success");
}
I am using Valums FileUploader and I am having trouble getting and reading a response after a file is uploaded. Basically I am trying to get a useful response to be used in onComplete, whether that be success or an error message.
I found another post here that said perhaps the server needs to be set to content-type plain/text. I checked that, and indeed that is the setting.
Anyway, been doing a lot of searching and finding various things to check, but nothing yet seems to solve my problem.
Here is some abbreviated code from the uploader:
var uploader = new qq.FileUploaderBasic({
button: document.getElementById('btnUpChange'),
action: templateURL+'/upload.php',
allowedExtensions: ['jpg', 'jpeg', 'png', 'gif'],
onSubmit: function(id, fileName) {
},
onComplete: function(id, fileName, responseJSON) {
var fileMsg = responseJSON;
console.log(fileMsg);
$('#filemsg').html('<span class="red" >'+fileMsg+'</span>');
}
});
And here is the text from the console after upload:
"[uploader] xhr - server response received"
"[uploader] responseText = File is too large"
[object Object]
I intentionally set the $sizeLimit small to throw an error just to try and get a message.
I took the php.php file included in the uploader zip, copied and renamed it, then added at the end this:
$allowedExtensions = array('jpg', 'jpeg', 'png', 'gif');
$sizeLimit = 4 * 1024;
$uploader = new qqFileUploader($allowedExtensions, $sizeLimit);
// Call handleUpload() with the name of the folder, relative to PHP's getcwd()
$result = $uploader->handleUpload('uploads/', $replaceOldFile = true);
if ($result['success'] !== true) {
echo $result['error'];
} else {
echo $result['success'];
}
If the upload is successful, I just get a 1 returned to the onComplete method. I tried to use responseJSON.responseText and all I got was "undefined".
Thank you for your help.
Found the solution to this...
First, on the php.php I put at the end this code:
echo htmlspecialchars(json_encode($result), ENT_NOQUOTES);
Then, in the onComplete function the JSON response can be returned like this:
onComplete: function(id, fileName, responseJSON) {
var newFile = responseJSON.filename;
var theError = responseJSON.error;
}
where the key name from the response array created in the php.php can be anything you want and is returned as in the example like this: responseJSON.keyname