File Upload with Ajax And Spring Secruity - ajax

After upgrading Spring Security to the 3.2.x version, the Ajax file upload function in my application is no longer working. Based on the SS 3.2 documentation, I append ${_csrf.parameterName}=${_csrf.token} on the ajax URL. That doesn't help. I also try SS post method setting as the followings without any luck either.
var token = $("meta[name='_csrf']").attr("value");
var header = $("meta[name='_csrf_header']").attr("value");
//...
$(document).ready(function() {
new AjaxUpload('#uploadButton', {
action: "/shop/admin/products/images/upload",
name: 'uploadData',
beforeSend : function(xhr) {
xhr.setRequestHeader("Accept","application/json");
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader(header, token);
},
onSubmit: function(file , ext) {
this.disable();
if (! (ext && /^(jpg|png|jpeg|gif|JPG|PNG|JPEG|GIF)$/.test(ext))){
alert('Error: invalid file extension');
return false; // cancel upload
}else {
// change button text, when user selects file
button.text(msg);
// If you want to allow uploading only 1 file at time,
// you can disable upload button
this.disable();
// Uploding -> Uploading. -> Uploading...
interval = window.setInterval(function(){
var text = button.text();
if (text.length < 13){
button.text(text + '.');
} else {
button.text(msg);
}
}, 200);
}
},
onComplete: function(file, response) {
button.text(msg);
window.clearInterval(interval);
this.enable();
}
});
});
And a html related code:
<button id="uploadButton" type="button" class="btn btn-default" th:text="#{label.upload}"></button>
The other option mentioned in the SS documentation is a filter configuration order. The Java configuration of the application doesn't come with any filter configuration. So this option can't be applied.
How to deal with this problem?

According to the latest Spring Security documentation, the code should be $("meta[name='_csrf']").attr("content") instead of $("meta[name='_csrf']").attr("value") which seems to make sense because the HTML meta tag has a content attribute instead of a value attribute.
Could you make this change and try again?

A normal form, but note the hidden CSRF:
<form id="myForm" action="upload" method="post">
<input type="file" name="file">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}">
<input type="submit" value="Upload file!">
</form>
Ajaxify with jQuery Form Plugin:
$(document).ready(function() {
$('#myForm').ajaxForm(function() {
alert("File was uploaded!");
});
});
Last step for success: Make sure you have MultipartFilter before Spring Security in your web.xml, as described in Spring Security reference, section CSRF Caveats.
jQuery Form Plugin has different strategies for file upload depending on browser, but the above should work regardless.

Related

Problem sending file as FormData to GraphQL HotChocolate server using fetch-api

I am trying to send form data to a graphql server using fetch-api
<div>
<!-- HTML5 Input Form Elements -->
<h4>GraphQL API Upload Test</h4>
<input id="fileUpload" type="file" name="fileUpload" />
<input id="fileDetail" type="text" name="fileDetail" />
<button id="upload-button" onclick="uploadFile()"> Upload </button>
<!-- Ajax JavaScript File Upload Logic -->
<script>
var queryTest = `mutation Header($input: Upload!) {
testUploadMutation( file: $input) {
response
}
}`;
async function uploadFile() {
let img = {};
img.file = fileUpload.files[0];
let formData = new FormData();
formData.append("operations",JSON.stringify( { query: queryTest}));
formData.append('map', { "0": ["variables.input"] });
formData.append('0', img.file);
await fetch("/graphql/", {
method: "POST",
body: formData
}).then(response => response.json()).then(data => console.log(data));
}
</script>
</div>
but I keep getting "Invalid JSON in the map multipart field; Expected type of Dictionary<string, string[]> from the server even though the mutation works fine on Banana Cake Pop. I have been using fetch-api to run all other queries and mutation successfully.
This is only code using formData with fetch-api for file upload and its not working . Anybody know what's going on?
Mutaion on Banana Cake Pop
mutation Header($input: Upload!) {
testUploadMutation( file: $input) {
response
}
}
#variables sent using the Banana Cake Pop Variables feature
{
"input": "somefile.jpg"
}
I have also tried sending formData.append('0', img)
instead of formData.append('0', img.file) but the same error

Unable to upload a file using Vue frontend and laravel server

I am trying to upload a file (File can be anything i.e. an image or pdf or doc, absolutely anything) . For that, I made a test form into my Vue component that is below
<form class="mt-5" method="post" enctype="multipart/form-data" id="uploadForm">
<div class="form-group">
<input type="file" id="test" name="test" class="form-control">
</div>
<button #click.prevent="uploadFile" type="button" class="btn btn-sm btn-primary">Upload</button>
</form>
Now when I submit form (click the upload button), I am running a function below
uploadFile() {
let something = $('#test').prop('files')[0];
console.log(something);
let form_data = new FormData();
form_data.append('file', something);
console.log(form_data);
axios.post('/upload/file', { form_data })
.then(() => {
console.log("done");
})
},
So when I console.log(something), it shows me the info of uploaded file but when I send data to server using axios and dd($request->all()) there, it shows me something => [] an empty array, that means, I am not getting the file and hence cant process it further to save it into my folders (upload).
What am I doing wrong here?
This happens because you are not setting the header Content-Type as multipart/form-data (and by default the application/json is being used) when making the request with axios, and the enctype attribute has no effect, since your not using the default form submit action. So try to pass a third argument in the post method, specifying the correct Content-Type header.
const uploadAvatar = ({ id, file }) => {
const formData = new FormData();
formData.append('avatar', file);
return axios.post(`users/${id}/avatar`, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
};

Upload File, file path null, VueJs / Laravel

I try to create a simple file upload with Vuejs and Laravel. But my 'file' variable it seems to be null on post.
<b-form-group label-for="file">
<b-form-file
v-model="file"
id="file"
:state="Boolean(file)">
</b-form-file>
</b-form-group>
data() {
return {
file: null,
},
}
and a method for post . (that works for the rest of the form)
addContract(newContract) {
post("/contracts/" + `${this.customer_id}`, newContract)
.then((response) => {
//
}
.catch(error){
//
}
}
Controller
//code
$fileName = time(). '.' .$request->$file->getClientOriginalExtention();
$request->file->move(public_path('upload'), $fileName);
dd($fileName);
//code
UPDATE
I set the axios header, but now I got error 422, when it was application I didn't but it still didn't work.
headers: {
'Content-Type': 'multipart/form-data'
}
In order for the file to upload via native browser submit, you must provide a name for the file input (along with adding enctype="multipart/form-data" to the enclosing <form> element).
<b-form-file
v-model="file"
id="file"
name="file"
:state="Boolean(file)">
</b-form-file>
This is the same requirement for any form <input>, <select>, or <textarea> control.

FineUploader uploading multiple files as one request

I need to upload multiple files as one request.
For example, i have two required files (.csv & .ctl) that I need to save.
Basically on the server side, I'm reading the .csv file and checking it against the .ctl file. If certain criteria doesn't match, I don't need to upload it. I'm not sure how or need to update the 'upload' method to read the filenames[]. Nor if I need to update this line "uploader.fineUploader('uploadStoredFiles');" to now accept the filenames[] after the user clicks "Upload now."
<script type="text/javascript">
$(document).ready(function () {
var filenames = [];
var uploader = $("#fine-uploader").fineUploader({
request: {
endpoint: '<%= ResolveUrl("~/Handler/UploadHandler.ashx")%>'
},
autoUpload: false,
text: {
uploadButton: '<span class="glyphicon glyphicon-plus"></span> Select Files'
},
validation: {
allowedExtensions: ['csv', 'ctl']
},
showMessage: function (message) {
// Using Bootstrap's classes
$('#fine-uploader').append('<div class="alert alert-danger">' + message + '</div>');
}
}).on('validate', function (event, fileData) {
return $.inArray(fileData.name, filenames) < 0;
}).on('submitted', function (event, fileId, fileName) {
filenames.push(fileName);
}).on('upload', function (event, fileId, fileName) {
var fileItemContainer = $(this).fineUploader('getItemByFileId', fileId);
$(this).fineUploader('setParams', { uploadType: 'VendorFileType', vendorId: '<%=vendorDropdownList1.CurrentVendorID %>' }, fileId);
}).on('complete', function (event, fileName, fileName, responseJSON) {
if (responseJSON.success) {
var div = document.getElementById('fine-uploader-status');
div.innerHTML = 'Upload process complete.';
}
else {
var div = document.getElementById('fine-uploader-status');
div.innerHTML = 'Upload denied.';
}
});
$('#uploadSelectedFiles').click(function () {
uploader.fineUploader('uploadStoredFiles');
});
});
</script>
//here's the aspx side.
<div id="fine-uploader">
</div>
<div id="fine-uploader-status">
</div>
<button id="uploadSelectedFiles" class="btn btn-primary">
<span class="glyphicon glyphicon-upload"></span>Upload now</button>
Fine Uploader does not support sending multiple files in a single request. This complicates the code unnecessarily and would break some existing features. Each file is sent in a separate request. You say you are performing some server-side checks to prevent uploads, but the files have already been uploaded by the time your server is able to perform these comparisons anyway. It's not clear from your question why you need to upload multiple files in a single request, or what benefit this gives you. If you clarify, perhaps I can provide alternate suggestions.

ASP.NET MVC 3 - File upload through AngularJS

I'm using AngularJS with my pages, and I have a doubt: when I do post with my form, how can I pass some selected file to my ASP.NET MVC 3 Controller?
Check this out:
My form:
<form enctype="multipart/form-data" ng-controller="FilesController" ng-submit="submitingForm()">
<div>
Choose the file:
<input type="file" onchange="angular.element(this).scope().setSelectedFile(this)" />
</div>
<input type="submit" value="Confirm" />
</form>
And the AngularJS Controller:
var module = angular.module('application', []);
(function (ang, app) {
function FilesController($scope, $http) {
$scope.setSelectedFile = function (element) {
$scope.$apply(function($scope) {
$scope.selectedFile = element.files[0];
});
};
$scope.submitingForm = function() {
$http.post(url, ???????).success(function() {
// How to pass that selected file for my ASP.NET controller?
});
}
}
app.controller("FilesController", FilesController);
})(angular, module);
Thank you!!!
I am not quite familiar with AngularJS but if you are attempting to upload a file using an AJAX request you can forget about it. That's not something that could be done. You could use the HTML5 File API if you want to upload files asynchronously to the server. There's an entire section dedicated to this.
And if your client browsers do not support the HTML5 File API you could use a File Upload plugin such as FineUploader or Uploadify (there are quite many others, just google).
My Approach is to use FormData.
Create a input tag like this.
<input id="imgInp" type="file" aria-label="Add photos to your post" class="upload" name="file" onchange="angular.element(this).scope().LoadFileData(this.files)" multiple="" accept="image/*" style="margin-top: -38px; opacity: 0; width: 28px;">
And in your Angular Controller create a method LoadFileData(inputFiles) as bellow:
var formData = new FormData();
$scope.LoadFileData = function (files) {
for (var file in files) {
formData.append("file", files[file]);
}
};
Now in formData variable you will have the uploaded files. Just send it to asp.net mvc controller.
$scope.submit = function () {
$http.post("/YourController/Method", formData, {
withCredentials: true,
headers: { 'Content-Type': undefined },
transformRequest: angular.identity
}).success(function (response) {
}
In Server side, Asp.NET MVC 4 controller you will have the files
var files = Request.Files;

Resources