How do I upload a file using POST in angular? - javascript

How do I transmit a pdf file? I have found multiple ways online, but most of them involve a rework of how our current system works, which is far from ideal.
Im not too familiar with angular, but I am trying to upload a file to the server. i am using existing architecture, so the issue isnt as easy as simply rewriting it from the ground up. Spring complains that "The current request is not a multipart request", if i try to send it as a multipart file, but I dont know how to make it one. The file type must be of type Blob. Currently, no error is thrown, but data.content is empty after the data block is transmitted.
Here is what I currently have:
$scope.uploadPDF = function(uploadedPDF) {
var url = 'uploadPDF';
data = {};
data.comments = $scope.worksheet.comments;
data.queryId = $scope.qId;
data.responseId = $scope.responseId;
data.requestTS = new Date().getTime();
data.content = uploadedPDF;
$http.post(url, data);
};
and the function that calls it is this, it pulls in the file, generates a name and adds the name as a property to be handled serverside, does some unaffiliated logic, then calls the above function for transmission:
$scope.addPDF = function() {
var pdfUploads = document.getElementById('file');
if ('files' in pdfUploads)
{
if (pdfUploads.files.length == 0)
{
$scope.setReasonForChange("addPDF");
}else
{
for (var i = 0; i < pdfUploads.files.length; i++)
{
var currentTimeZone = new Date().toLocaleTimeString('en-us',{timeZoneName:'short'}).split(' ')[2];
$scope.militaryTime = $filter('date')(Date.now(), "MM-dd-yyyy_HHmm");
pdfUploads.files[i].generatedFileName = "QID-" + $scope.queryId + "_" + $scope.worksheet.response.PDF_DESCRIPTION + "_" + $scope.militaryTime + currentTimeZone + ".PDF";
}
}
}
var pdfComment = document.getElementById("pdfComment").value;
if (!pdfComment)
{
$scope.setReasonForChange("updatePDF");
} else
{
var blobPDF = new Blob([pdfUploads.files[0]], {type: 'application/pdf'});
$scope.uploadPDF(blobPDF);
}
}
HTML is:
<form name="UploadForm" id="UploadForm" class="details" form-on-change="formChanged()" enctype="multipart/form-data">
<input type="file" multiple size="50" id="file" name="file" ng-disabled="is_readonly"/>
<button ng-click="addPDF()" ng-disabled="is_readonly">Add</button>
</form>
And lastly, serverside is this, where i think data is part of a linkedhashmap, where the values are taken from in the server, and processed:
#ResponseBody
#RequestMapping(value = "/uploadPDF", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseAttachment uploadPDF(#RequestBody Data data, HttpServletRequest request) throws Exception {
User user = (user) request.getSession(false).getAttribute(FieldConstants.USER_SESSION_ATTR);
ResponseAttachment newPDF = responseAttachmentService.addAttachment(data, user.getUserId());
return newPDF;
Currently, it transmits and receives the data, except the place where the file is supposed to be is empty.
I have attempted ng-fileupload, but attaching it to our product is a nightmare, especially considering that its use kinda requires the user to already know how to use angular as it has little documentation... and we have no angular people.

This question may help you.
Basically you can't send files in purely a JSON format. You have to use a multipart form and post it that way. For example:
postFile(file) {
var postData = new FormData();
postData.append('File', file);
var params = {
headers: {
"Content-Type": undefined
}
$http.post(url, data, params).then([...]);
}
You'll need the extra Content-Type param so that it is sent properly.

Related

JS - Intercept attribute changes

My goal here is to intercept all attribute setting and changes. Problem is that I don't know how to do that. I wanna change attributes that set / change like this
cpo.src = "/cdn-cgi/challenge-platform/orchestrate/jsch/v1"; not using setAttribute(src, 'stufz')
I have some code that could be used from my Ajax interceptor that works 100% and changes the request. Basically why I am doing this is I am making a web proxy and it needs great rewriting. Please client-side JS only! Pure Javascript too!
Basically heres an example of how I wanna intercept it
/cdn-cgi/challenge-platform/orchestrate/jsch/v1 --> /alloy?url=https://soap2day.to/cdn-cgi/challenge-platform/orchestrate/jsch/v1
If I am not being clear enough please explain in the comments.
// Ajax Interceptor that works perfectly that could be used in this case.
let oldXHROpen = window.XMLHttpRequest.prototype.open;window.XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
if (url.startsWith('http')) {
const encodedURL = btoa(url)
url = '/alloy/?url=' + encodedURL
} else if (url.startsWith('//')) {
const encodedURL = btoa('http:' + url)
url = '/alloy/?url=' + encodedURL
} else if (url.startsWith('/')) {
if (!url.startsWith('/fetch/')) {
let apData = document.getElementById('alloyData');
let urlData = apData.getAttribute('data-alloyURL');
url = '/fetch/' + urlData + url
}
}
return oldXHROpen.apply(this, arguments);
}```

Better way to pass data to server?

So I am using query strings to pass data from a form to my server. The query strings look like this:
this.$http.post('http://localhost:3000/operation?Stime='+this.Stime+'&Etime='+this.Etime+'&eAMPM='+this.eAMPM+'&sAMPM='+this.sAMPM+'&id='+this.id+'&activity='+this.activity+'&auto_insert='+this.auto_insert+'&yearmonthday='+this.yearmonthday+'&color1='+this.c)
and In my server I have all these variables to store the query's variables:
var color = req.query.color1;
var Stime = req.query.Stime;
var Etime = req.query.Etime;
var sAMPM = req.query.sAMPM;
var eAMPM = req.query.eAMPM;
var id = req.query.id;
var activity = req.query.activity;
var requestAccepted = req.query.requestAccepted;
var yearmonthday = req.query.yearmonthday;
var auto_insert = req.query.auto_insert;
It just seems like a lot of code to just post my variables to the server (It works just fine) but I was wondering if there were some ways I could refactor it/ make it cleaner
Of course there is!
Consider doing some research into the HTTP message body:
A message body is the one which carries the actual HTTP request data (including form data and uploaded, etc.) and HTTP response data from the server ( including files, images, etc.).
In your case, you can change your Angular $http POST request to as follows -
var data = {
Stime: '+this.Stime+',
Etime: '+this.Etime+',
eAMPM: '+this.eAMPM+',
sAMPM: '+this.sAMPM+',
id: '+this.id+',
activity: '+this.activity+',
auto_insert: '+this.auto_insert+',
yearmonthday: '+this.yearmonthday+',
color1: '+this.c+'
}
$http({
method: 'POST',
url: 'http://localhost:3000/operation',
data: JSON.stringify(data)
})

passing php variables with ajax

I've been trying to get php to rename an image in an S3 bucket. There's three stages
upload image (you can see a previous question I asked to see my
solution to that) I've included the code I used below.
Use ajax to take the name I want for a file (the user ID) and
pass it to a PHP renaming script.
run the renaming script.
Step two is giving me problems. If i hard code the names into the PHP script then it will find the source file and rename it.
However I can't get it to rename the files using variables drawn from the page.
Here's the HTML button code
<input type="file" id="file-chooser" />
<button onclick= "pass()" id="upload-button">Upload to S3</button>
<div id="results"></div>
Here's the JS code
<script type="text/javascript">
var bucket = new AWS.S3({params: {Bucket: 'MY BUCKET'}});
var fileChooser = document.getElementById('file-chooser');
var button = document.getElementById('upload-button');
var results = document.getElementById('results');
button.addEventListener('click', function() {
var file = fileChooser.files[0];
if (file) {
results.innerHTML = '';
var filename = file.name;
var params = {Key: file.name, ContentType: file.type, Body: file};
bucket.upload(params, function (err, data) {
results.innerHTML = err ? 'ERROR!' : 'UPLOADED';
ajax_post();
});
} else {
results.innerHTML = 'Nothing to upload.';
}
}, false);
function pass() {
$.get("test.php");
return false;
}
function ajax_post() {
var ref = new
Firebase("https://MY FIREBASE .firebaseio.com/");
var authData = ref.getAuth();
// Create our XMLHttpRequest object
var fileChooser = document.getElementById('file-chooser');
var file = fileChooser.files[0];
var filename = file.name;
var userID = authData.uid;
var userID = USERS USER ID;
//alert($(this).attr('id'));
$.ajax({
type: "POST",
url: 'test.php',
data: { userID : userID },
data: { filename : filename },
success: function (data) {
alert("success!");
}
};
pass();
}
</script>
Here's the PHP.
<?php
require 'vendor/autoload.php';
use Aws\S3\S3Client;
$sourceBucket = "MY BUCKET";
$sourcename1 = $_POST['filename'];
$targetKeyname = $_POST['userID'];
$targetBucket = "MY BUCKET";
$s3 = S3Client::factory(array(
'key' => "MY KEY",
'secret' => "MY SECRET KEY"
));
$s3->copyObject(array(
'Bucket' => $targetBucket,
'Key' => $targetKeyname,
'CopySource' => "{$sourceBucket}/{$sourcename}",
));
?>
EDITING TO ADD
I've been running test after test. If I hard code the variables into the PHP file it works. If I hard code the variables into the JS script it fails. The ajax is running the php file it's just not passing the variables to it. I've tried with and without the ISSET on the PHP side it just fails to take in the variables each time.
Any ideas?
I suspect this is your issue var userID = USERS USER ID; I'm not sure where that information is coming from. But without seeing the html/js where that is derived from, its difficult to determine the problem.
If its text input with an id of userID, it should be something like:
<input type="text" name="userID" id="userID">
js:
var userID = $("#userID").val();
AJAX Update
merge BOTH data into one object:
data: { userID : userID },
data: { filename : filename },
will become
data: { userID : userID , filename : filename },
I found out what the issue was. It was Firebase. Firebase was returning the values I required however the PHP script was retrieving an error from AWS. I had assumed that the AJAX was failing to pass the value on but I was only half right. the AJAX failed because it didn't have the value at that point. I added in a 3 second delay before the upload occurs and it works fine. Firebase was just slower than the PHP.

How to pass FormData over XMLHttpRequest using GET method

When Method of the senderform is POST, everything works fine. However, as soon as I change the method to GET, I don't receive anything on the server.
function ajaxSubmit(destinationElement, senderform) {
var xmlreq = new XMLHttpRequest();
var params = new FormData(senderform);
xmlreq.open(senderform.method, senderform.action, true);
if (/\/content\.php$/.test(senderform.action))
xmlreq.onreadystatechange = receiveTable;
else xmlreq.onreadystatechange = receiveText;
xmlreq.send(params);
}
I know that I could manually append key-value pairs at the end of Action address, but the problem is that I don't know which form is going to be passed with what fields.
I would prefer native javaScript if possible.
How can I send a GET request using XMLHttpRequest with key-value pairs from senderform which points to form Element (the same way as it already works for POST requests)?
First parameter is a reference to submit button, or form element itself. Second is callback function for XMLHttpRequest.
var ajaxSubmit = function(sender, callback) {
var xmlreq = new XMLHttpRequest(), params;
// look around for the sender form and key-value params
if (sender.form !== undefined)
{
params = new FormData(sender.form);
params.append(sender.name, sender.value);
sender = sender.form;
}
else params = new FormData(sender);
var actAddress = sender.action;
// append the params to the address in action attribute
if (sender.method == 'get')
{
var firstRun = true;
for (var key of params.keys())
{
if (firstRun)
{
actAddress += '?';
firstRun = false;
}
else actAddress += '&';
actAddress += key + "=" + params.get(key);
}
}
xmlreq.open(sender.method, actAddress, true);
xmlreq.onreadystatechange = callback;
if (sender.method == 'get')
xmlreq.send();
else xmlreq.send(params);
}
Therefore you can use it as
<form onsubmit="ajaxSubmit(this,callbackFx)" >
<!-- or -->
<input onclick="ajaxSubmit(this,callbackFx)" type="submit" name="" value=""/>
</form>
Are you sure the problem is not the PHP script? I see no reference that https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#send() with FormData needs POST to work, but if the PHP script takes the info from $POST or something (My PHP is rusty), the behavior would be different.
Since you can't create a useable body in a GET request (see below), then the other option is to use params in the url.
function buildGetUrlParams(baseUrl, paramsObj) {
var builtUrl = baseUrl + "?";
Object.keys(paramsObj).forEach(function(key) {
builtUrl += key + "=" + paramsObj[key] + "&";
});
return builtUrl.substr(0, builtUrl.length - 1);
}
document.getElementById('finalUrl').innerText = buildGetUrlParams('http://test.url.com', { name:'James', occupation:'web design' });
<div id="finalUrl"></div>
An HTTP GET request can contain a body, but there is no semantic meaning to that body. Which means, in simple terms, that a server doesn't have any reason to, nor have any knowledge of how, to process the body of a GET request. If it's possible to write a server that could do this, it would be bad practice as per the HTTP/1.1 specs:
if the request method does not include defined semantics for an entity-body, then the message-body SHOULD be ignored when handling the request.
And that's basically why it's not working. If you want to send any sort of data that the server is able to respond to, then you'll need to use a different HTTP method.
This answer also explains this issue.

Struts2 Action Form Data Maximum Size for Image Upload using Base64

I have a Struts2 action that receives a string containing an image in Base64 and another string for the image name.
Everything works well for small sized images. But when I try to send a larger image, the Base64 and the image name strings are set to null in the action implementation.
In search for a solution I found that the default maximum size for the file upload is 2 MB. This limit can be increased with the following properties:
<constant name="struts.multipart.maxSize" value="104857600" />
<param name="maximumSize">52428800</param>
<param name="fileUpload.maximumSize">52428800</param>
However this is not working. Probably this implementation is not a file upload but a two string POST request.
Is there a way I can increase the size of the post request? Or the problem is something else?
public class GalleryAction extends BaseAction {
private String imageBase64;
private String imageName;
...
public final String uploadOperation() throws Exception {
if (this.imageBase64 == null || this.imageBase64.length() == 0
|| this.imageName == null || this.imageName.length() == 0) {
throw new Exception("Invalid argument");
}
byte[] decodedBytes = Base64Decoder.decode2bytes(this.imageBase64);
InputStream is = new ByteArrayInputStream(decodedBytes);
Graphics graphics = MGraphics.insertImageToDataBase(this.imageName, is);
// Issue server to sync image.
RestUtils.syncImage(graphics.getId());
JSONObject response = new JSONObject();
response.put("statusCode", "0");
jsonString = response.toString();
return JSON_RESPONSE;
}
...
}
EDIT:
I forgot to publish the code for the image upload.
Gallery.prototype.upload = function(base64, imageName) {
var galleryObject = this;
galleryObject.loadingCallback(true);
$.post(this.urlBase + "/upload", {
"imageBase64" : base64.match(/,(.*)$/)[1],
"imageName" : imageName
}, function(data) {
galleryObject.handlerErrorMessageCallback();
galleryObject.loadingCallback(false);
galleryObject.refreshImageGalleryCallback();
}, "json")
.fail(function() {
galleryObject.handlerErrorMessageCallback("error.upload.image");
galleryObject.loadingCallback(false);
});
}
The HTTP protocol specifications don't set a limit to the size of a POST message. However, your application server does it (mainly to prevent DDoS attacks).
Usually this threshold is 10 MegaBytes, and it is the one you are hitting. You should then be able to customize this setting according to your AS specs.
That said, this is not encouraged, and could lead to security vulnerabilities.
The best thing would be to:
use multipart/form-data over application/x-www-form-urlencoded;
use File instead of String since what you're uploading are files, and not strings.
I changed the upload method to Form data.
Gallery.prototype.upload = function(base64, imageName) {
var galleryObject = this;
galleryObject.loadingCallback(true);
var request = new FormData();
request.append("imageBase64", base64.match(/,(.*)$/)[1]);
request.append("imageName", imageName);
$.ajax({url:this.urlBase + "/upload",
data: request,
type: "POST",
processData: false,
contentType: false,
success: function(result)
{
galleryObject.handlerErrorMessageCallback();
galleryObject.loadingCallback(false);
galleryObject.refreshImageGalleryCallback();
}
}).fail(function() {
galleryObject.handlerErrorMessageCallback("error.upload.image");
galleryObject.loadingCallback(false);
});
}
Using this post method the following property on strus.xml must exists
<constant name="struts.multipart.maxSize" value="104857600" />

Categories