Facebook Graph API - upload photo using JavaScript - javascript

Is it possible to upload a file using the Facebook Graph API using javascript, I feel like I'm close. I'm using the following JavaScript
var params = {};
params['message'] = 'PicRolled';
params['source'] = '#'+path;
params['access_token'] = access_token;
params['upload file'] = true;
function saveImage() {
FB.api('/me/photos', 'post', params, function(response) {
if (!response || response.error) {
alert(response);
} else {
alert('Published to stream - you might want to delete it now!');
}
});
}
Upon running this I receive the following error...
"OAuthException" - "(#324) Requires upload file"
When I try and research this method all I can find out about is a php method that apears to solve this
$facebook->setFileUploadSupport(true);
However, I am using JavaScript, it looks like this method might be to do with Facebook Graph permissions, but I already have set the permissions user_photos and publish_stream, which I believed are the only ones I should need to perform this operation.
I have seen a couple of unanswered questions regarding this on stackoverflow, hopefully I can explained myself enough. Thanks guys.

Yes, this is possible, i find 2 solutions how to do that and they are very similar
to each other, u need just define url parameter to external image url
FIRST one using Javascript SDk:
var imgURL="http://farm4.staticflickr.com/3332/3451193407_b7f047f4b4_o.jpg";//change with your external photo url
FB.api('/album_id/photos', 'post', {
message:'photo description',
url:imgURL
}, function(response){
if (!response || response.error) {
alert('Error occured');
} else {
alert('Post ID: ' + response.id);
}
});
and SECOND one using jQuery Post request and FormData:
var postMSG="Your message";
var url='https://graph.facebook.com/albumID/photos?access_token='+accessToken+"&message="+postMSG;
var imgURL="http://farm4.staticflickr.com/3332/3451193407_b7f047f4b4_o.jpg";//change with your external photo url
var formData = new FormData();
formData.append("url",imgURL);
$.ajax({
url: url,
data: formData,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function(data){
alert("POST SUCCESSFUL");
}
});

EDIT: this answer is (now) largely irrelevant. If your image is on the web, just specify the url param as per the API (and see examples in other answers). If you would like to POST the image content to facebook directly, you may want to read this answer to gain understanding. Also see HTML5's Canvas.toDataUrl().
The API says: "To publish a photo, issue a POST request with the photo file attachment as multipart/form-data."
FB is expecting that the bytes of the image to be uploaded are in the body of the HTTP request, but they're not there. Or to look at it another way - where in the FB.api() call are you supplying the actual contents of the image itself?
The FB.api() API is poorly documented, and doesn't supply an example of an HTTP POST which includes a body. One might infer from the absence of such an example that it doesn't support this.
That's probably OK - FB.api() is using something called XmlHttpRequest under the covers which does support including a body ... look it up in your favourite JavaScript reference.
However, you'll still have 2 sub-problems to solve:
how to prepare the image bytes (and rest of the request) as multipart/form-data; and
getting the bytes of the image itself
(incidentally, the need to encode the message body is probably what the PHP setFileUploadSupport(true) method is for - tell the facebook object to encode the message body as multipart/form-data before sending)
But it's a bit meessier than that
Unfortunately, sub-problem '2' may bite you - there is no way (last time I looked) to extract the bytes of an image from the browser-supplied Image object.
If the image to be uploaded is accessible via a URL, you could fetch the bytes with XmlHttpRequest. Not too bad.
If the image is coming from the user's desktop, your probable recourse is to offer the user a:
<form enctype="multipart/form-data">
<input type="filename" name="myfile.jpg" />
<input type="hidden" name="source" value="#myfile.jpg"/>
<input type="hidden" name="message" value="My Message"/>
<input type="hidden" name="access_token" value="..."/>
</form>
(notice that source references the name given to the file-upload widget)
... and hope that FB anticipated receiving the data in this manner (try it with a static HTML form first, before coding it up dynamically in JS). One might infer that in fact it would, since they don't offer another means of doing it.

i used #Владимир Дворник code with some modification, I had the same issue and with this code it worked very well:
var imgURL = //your external photo url
FB.api('/photos', 'post', {
message: 'photo description',
access_token: your accesstoken
url: imgURL
}, function (response) {
if (!response || response.error) {
alert('Error occured:' + response);
} else {
alert('Post ID: ' + response.id);
}
});

Photos can be uploaded to facebook profile using Ajax as follows.
$.ajax({
type: "POST",
url: "https://graph.facebook.com/me/photos",
data: {
message: "Your Msg Goes Here",
url: "http://www.knoje.com/images/photo.jpg[Replace with yours]",
access_token: token,
format: "json"
},
success: function(data){
alert("POST SUCCESSFUL"); }
});
So this is the best way to post photo to a facebook profile with GRAPH API and is the simple one.
In many answer i have seen that image url is shwon by the source,picture or image etc but that doesn't works.
The use of of source,picture or image leads to a (#324) Requires upload file error .
Best way to avoid the 324 error.

Only #Thiago's answer is answering the question of uploading data via javascript. I've found that the Facebook JS API doesn't cover this situation.
I've also brew & tested my personl solution.
Main steps
Get the binary data of the image (I've used a canvas, but using an input box is possible as well)
Form a multipart request with all necesarry data for the graph API call
Include the binary data in the request
Encode everything in a binary array and send it so via XHR
Code
Conversion utilities
var conversions = {
stringToBinaryArray: function(string) {
return Array.prototype.map.call(string, function(c) {
return c.charCodeAt(0) & 0xff;
});
},
base64ToString: function(b64String) {
return atob(b64String);
}
};
Image posting snippet
var DEFAULT_CALL_OPTS = {
url: 'https://graph.facebook.com/me/photos',
type: 'POST',
cache: false,
success: function(response) {
console.log(response);
},
error: function() {
console.error(arguments);
},
// we compose the data manually, thus
processData: false,
/**
* Override the default send method to send the data in binary form
*/
xhr: function() {
var xhr = $.ajaxSettings.xhr();
xhr.send = function(string) {
var bytes = conversions.stringToBinaryArray(string);
XMLHttpRequest.prototype.send.call(this, new Uint8Array(bytes).buffer);
};
return xhr;
}
};
/**
* It composes the multipart POST data, according to HTTP standards
*/
var composeMultipartData = function(fields, boundary) {
var data = '';
$.each(fields, function(key, value) {
data += '--' + boundary + '\r\n';
if (value.dataString) { // file upload
data += 'Content-Disposition: form-data; name=\'' + key + '\'; ' +
'filename=\'' + value.name + '\'\r\n';
data += 'Content-Type: ' + value.type + '\r\n\r\n';
data += value.dataString + '\r\n';
} else {
data += 'Content-Disposition: form-data; name=\'' + key + '\';' +
'\r\n\r\n';
data += value + '\r\n';
}
});
data += '--' + boundary + '--';
return data;
};
/**
* It sets the multipart form data & contentType
*/
var setupData = function(callObj, opts) {
// custom separator for the data
var boundary = 'Awesome field separator ' + Math.random();
// set the data
callObj.data = composeMultipartData(opts.fb, boundary);
// .. and content type
callObj.contentType = 'multipart/form-data; boundary=' + boundary;
};
// the "public" method to be used
var postImage = function(opts) {
// create the callObject by combining the defaults with the received ones
var callObj = $.extend({}, DEFAULT_CALL_OPTS, opts.call);
// append the access token to the url
callObj.url += '?access_token=' + opts.fb.accessToken;
// set the data to be sent in the post (callObj.data = *Magic*)
setupData(callObj, opts);
// POST the whole thing to the defined FB url
$.ajax(callObj);
};
Usage
postImage({
fb: { // data to be sent to FB
caption: caption,
/* place any other API params you wish to send. Ex: place / tags etc.*/
accessToken: 'ACCESS_TOKEN',
file: {
name: 'your-file-name.jpg',
type: 'image/jpeg', // or png
dataString: image // the string containing the binary data
}
},
call: { // options of the $.ajax call
url: 'https://graph.facebook.com/me/photos', // or replace *me* with albumid
success: successCallbackFunction,
error: errorCallbackFunction
}
});
Extra
Extracting the binary string representation of a canvas image
var getImageToBeSentToFacebook = function() {
// get the reference to the canvas
var canvas = $('.some-canvas')[0];
// extract its contents as a jpeg image
var data = canvas.toDataURL('image/jpeg');
// strip the base64 "header"
data = data.replace(/^data:image\/(png|jpe?g);base64,/, '');
// convert the base64 string to string containing the binary data
return conversions.base64ToString(data);
}
Information on how to load the binaryString from an input[type=file]
HTML5 File API read as text and binary
Notes:
There are of course alternative approaches as well
Using an HTML form in an iframe - you cannot get the response from the call
Using a FormData & File approach, but unfortunately in this case there are a lot of incompatilities which make the process harder to use, and you would end up duct-taping around the inconsistencies - thus my choice was manual data assembly since HTTP standards rarely change :)
The solution does not require any special HTML5 features.
The above example uses jQuery.ajax, jQuery.extend, jQuery.each

Yes, you can do this posting data to an iframe like here, or you can use jQuery File Upload .
The problem is you can't get response from iframe, using plugin you can use a page handle.
Example: upload a video using jQuery File Upload
<form id="fileupload" action="https://graph-video.facebook.com/me/photos" method="POST" enctype="multipart/form-data">
<input type="hidden" name="acess_token" value="user_acess_token">
<input type="text" name="title">
<input type="text" name="description">
<input type="file" name="file"> <!-- name must be file -->
</form>
<script type="text/javascript">
$('#fileupload').fileupload({
dataType: 'json',
forceIframeTransport: true, //force use iframe or will no work
autoUpload : true,
//facebook book response will be send as param
//you can use this page to save video (Graph Api) object on database
redirect : 'http://pathToYourServer?%s'
});
</script>

To upload a file from the local computer with just Javascript try HelloJS
<form onsubmit="upload();">
<input type="file" name="file"/>
<button type="submit">Submit</button>
</form>
<script>
function upload(){
hello.api("facebook:/me/photos", 'post', document.getElementById('form'), function(r){
alert(r&&!r.error?'Success':'Failed');
});
}
</script>
There's an upload demo at http://adodson.com/hello.js/demos/upload.html

https://stackoverflow.com/a/16439233/68210 contains a solution that works if you need to upload the photo data itself and don't have a url.

This still works. I am using it as below:
var formdata= new FormData();
if (postAs === 'page'){
postTo = pageId; //post to page using pageID
}
formdata.append("access_token", accessToken); //append page access token if to post as page, uAuth|paAuth
formdata.append("message", photoDescription);
formdata.append("url", 'http://images/image.png');
try {
$.ajax({
url: 'https://graph.facebook.com/'+ postTo +'/photos',
type: "POST",
data: formdata,
processData: false,
contentType: false,
cache: false,
error: function (shr, status, data) {
console.log("error " + data + " Status " + shr.status);
},
complete: function () {
console.log("Successfully uploaded photo to Facebook");
}
});
} catch (e) {
console.log(e);
}
I have to ask though if you people have any idea if this is advisable or has a big security risk compared to using PHP api for Facebook.

This works:
function x(authToken, filename, mimeType, imageData, message) {
// this is the multipart/form-data boundary we'll use
var boundary = '----ThisIsTheBoundary1234567890';
// let's encode our image file, which is contained in the var
var formData = '--' + boundary + '\r\n';
formData += 'Content-Disposition: form-data; name="source"; filename="' + filename + '"\r\n';
formData += 'Content-Type: ' + mimeType + '\r\n\r\n';
for (var i = 0; i < imageData.length; ++i) {
formData += String.fromCharCode(imageData[i] & 0xff);
}
formData += '\r\n';
formData += '--' + boundary + '\r\n';
formData += 'Content-Disposition: form-data; name="message"\r\n\r\n';
formData += message + '\r\n';
formData += '--' + boundary + '--\r\n';
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://graph.facebook.com/me/photos?access_token=' + authToken, true);
xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
// Solving problem with sendAsBinary for chrome
try {
if (typeof XMLHttpRequest.prototype.sendAsBinary == 'undefined') {
XMLHttpRequest.prototype.sendAsBinary = function(text) {
var data = new ArrayBuffer(text.length);
var ui8a = new Uint8Array(data, 0);
for (var i = 0; i < text.length; i++) ui8a[i] = (text.charCodeAt(i) & 0xff);
this.send(ui8a);
}
}
} catch (e) {}
xhr.sendAsBinary(formData);
};

I seem to have a similar problem, but solutions above didn't work.
I was using the solution suggested by Arrabi (just use the url property only) to post images without any problem. My images are around 2-3 MB each.
When I migrated my app to another server (changing the absolute url of my images in the post) the method kept giving me 324 errors for images above around 100k size.
I thought it was due to some Apache setting on my end, but when I changed apache for lighttpd the problem was still there.
The connections from Facebook actually show up in my (apache) log:
69.171.234.7 - - [08/Jun/2012:11:35:54 +0200] "GET /images/cards/1337701633_518192458.png HTTP/1.1" 200 2676608 "-" "facebookplatform/1.0 (+http://developers.facebook.com)"
69.171.228.246 - - [08/Jun/2012:11:42:59 +0200] "GET /images/test5.jpg HTTP/1.1" 200 457402 "-" "facebookplatform/1.0 (+http://developers.facebook.com)"
69.171.228.246 - - [08/Jun/2012:11:43:17 +0200] "GET /images/test4.jpg HTTP/1.1" 200 312069 "-" "facebookplatform/1.0 (+http://developers.facebook.com)"
69.171.228.249 - - [08/Jun/2012:11:43:49 +0200] "GET /images/test2.png HTTP/1.1" 200 99538 "-" "facebookplatform/1.0 (+http://developers.facebook.com)"
69.171.228.244 - - [08/Jun/2012:11:42:31 +0200] "GET /images/test6.png HTTP/1.1" 200 727722 "-" "facebookplatform/1.0 (+http://developers.facebook.com)"
Only test2.png succeeded.

I use the following to share a photo (some BitmapData from the Phaser framework). It seems to work...
// Turn data URI to a blob ready for upload.
dataURItoBlob(dataURI:string): Blob {
var byteString = atob(dataURI.split(',')[1]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], { type: 'image/jpeg' });
}
// Share the given bitmapData as a photo on Facebook
sharePhoto(accessToken: string, photo: BitmapData, message: string): void {
// Create form data, set up access_token, source and message
var fd = new FormData();
fd.append("access_token", accessToken);
fd.append("source", this.dataURItoBlob(photo.canvas.toDataURL("image/jpeg")));
fd.append("message", message);
var request = new XMLHttpRequest();
var thisPtr = this;
request.onreadystatechange = function () {
if (request.readyState == XMLHttpRequest.DONE) {
var json = JSON.parse(request.responseText);
if (json.hasOwnProperty("error")) {
var error = json["error"];
if (error.hasOwnProperty("type")) {
var errorType = error["type"];
if (errorType === "OAuthException") {
console.log("Need to request more permissions!");
}
}
}
} else if (request.readyState == XMLHttpRequest.HEADERS_RECEIVED) {
} else if (request.readyState == XMLHttpRequest.LOADING) {
} else if (request.readyState == XMLHttpRequest.OPENED) {
} else if (request.readyState == XMLHttpRequest.UNSENT) {
}
}
request.open("POST", "https://graph.facebook.com/me/photos", true);
request.send(fd);
}

In case anyone still looking for how to upload directly from canvas to Facebook photos, this works for me:
function postImageToFacebook(token, imageData, message, successCallback, errorCallback) {
var fd = new FormData();
fd.append("access_token", token);
fd.append("source", imageData);
fd.append("caption", message);
$.ajax({
url: "https://graph.facebook.com/me/photos?access_token=" + token,
type: "POST",
data: fd,
processData: false,
contentType: false,
cache: false,
success: function (data) {
successCallback(data);
},
error: function (shr, status, data) {
errorCallback(data);
},
complete: function (data) {
console.log('Completed');
}
});
}
function dataURItoBlob(dataURI) {
var byteString = atob(dataURI.split(',')[1]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], {type: 'image/jpeg'});
}
To use it
// *IMPORTANT*
var FBLoginScope = 'publish_actions'; // or sth like 'user_photos,publish_actions' if you also use other scopes.
var caption = "Hello Facebook!";
var successCallback = ...;
var errorCallback = ...;
var data = $('#your_canvas_id')[0].toDataURL("image/jpeg");
try {
imageData = dataURItoBlob(data);
} catch (e) {
console.log(e);
}
FB.getLoginStatus(function (response) {
if (response.status === "connected") {
postImageToFacebook(response.authResponse.accessToken, imageData, caption, successCallback, errorCallback);
} else if (response.status === "not_authorized") {
FB.login(function (response) {
postImageToFacebook(response.authResponse.accessToken, imageData, caption, successCallback, errorCallback);
}, {scope: FBLoginScope});
} else {
FB.login(function (response) {
postImageToFacebook(response.authResponse.accessToken, imageData, caption, successCallback, errorCallback);
}, {scope: FBLoginScope});
}
});
Modified from: http://gorigins.com/posting-a-canvas-image-to-facebook-and-twitter/

Related

Get PDF from WebAPI and download from UI, but data gets corrupted

I call a Web API Controller from my UI which then gets a report from SSRS. It inserts the bytes in the content of the response and sends it to the UI where it gets downloaded as a PDF.
Inside my Web API Controller I write the report bytes to a test PDF file to inspect the contents of the pdf and to see if the data is correct, which it is. But when the PDF gets downloaded from my UI and I open it, I get a blank paged document. When I inspect the reponse content in Fiddler, I can see that the data is corrupted and doesn't match the test PDF file data.
Server side:
[HttpPost]
public HttpResponseMessage GetInstancePdf(InstancePdfModel model) {
var bytes = _digitalFormService.GetInstancePdf(model.ClientGuid, model.InstanceGuid, model.InstanceVersion);
File.WriteAllBytes(# "c:\temp\test.pdf", bytes);
var response = Request.CreateResponse();
response.Content = new ByteArrayContent(bytes);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue(DispositionTypeNames.Inline) {
FileName = "file.pdf"
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return response;
}
Client side:
$scope.downloadPdf = function(instance) {
$scope.isBusy = true;
digitalFormService.getInstancePdf(instance.instanceGuid, instance.instanceVersion).then(function(data) {
if (data.status === 200) {
const file = new Blob([data.data], {
type: data.headers("Content-Type")
});
if (navigator.appVersion.toString().indexOf(".NET") > 0) {
window.navigator.msSaveBlob(file, (`${instance.name} ${(new Date()).toLocaleString()}`).replace(",", ""));
} else {
//trick to download, store a file having its URL
const fileUrl = URL.createObjectURL(file);
const a = document.createElement("a");
a.href = fileUrl;
a.target = "_blank";
a.download = (`${instance.name} ${(new Date()).toLocaleString()}`).replace(",", "");
document.body.appendChild(a);
a.click();
}
} else {
debugger;
}
$scope.isBusy = false;
});
};
function getInstancePdf(instanceGuid, instanceVersion) {
var data = {
clientGuid: digitalFormConfig.clientToken,
instanceGuid: instanceGuid,
instanceVersion: instanceVersion
};
return $http({
url: digitalFormConfig.serverUrl +
"api/DigitalForm/GetInstancePdf",
dataType: "json",
data: data,
method: "POST"
}).then(function(response) {
return response;
},
function() {
return $q.reject("No Data");
});
}
I expect my downloaded PDF to be an informational document, matching the test PDF file saved inside the Web API Controller, but I get a blank document instead (same number of pages as test file, but blank).
I used Fiddler to inspect the response body. When I save the response body from within Fiddler as a pdf - everything is fine. So I am sure my server side code is correct. The problem must be somewhere on the client side.
Any help? Thanks.
I found the mistake. The bug was in the client side service. Code should look as follows:
function getInstancePdf(instanceGuid, instanceVersion) {
var data = {
clientGuid: digitalFormConfig.clientToken,
instanceGuid: instanceGuid,
instanceVersion: instanceVersion
};
return $http({
responseType: "arraybuffer",
url: digitalFormConfig.serverUrl +
"api/DigitalForm/GetInstancePdf",
dataType: "json",
data: data,
method: "POST"
}).then(function (response) {
return response;
},
function () {
return $q.reject("No Data");
});
}
The line responseType: "arraybuffer", was omitted previously.

Mixed Content Error (Http/Https)

I have mixed content error, on web site used both http and https protocols.
Here's the error from Chrome console:
Mixed Content: The page at 'https://www.amazon.com/' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://www.amazon.com/../?redirect=true'. This request has been blocked; the content must be served over HTTPS.
Here's screenshot with the error: http://prntscr.com/9os5li
Was found some solution like:
Change link from "http://" to "https://" in
Blocked loading mixed active content.
Nothing helped me, because Amazon server drop it all the time when I change link in code or manual from http to https drop it and make it as http.
For example this one Link 2 I can't use here https, because of this I have mixed content error.
Here's my AJAX where I make a call:
$.ajax({
url: "//" + MWS_URL + rest_path,
data: request,
dataType: 'text',
type: 'POST',
contentType: 'application/x-www-form-urlencoded; charset=utf-8',
beforeSend: function(req) {
//req.setRequestHeader("User-Agent", "chrome extension");
req.setRequestHeader("x-amazon-user-agent", "chrome extension");
},
success: function(data){
if (onSuccess) {
onSuccess(data);
}
},
error: function(jqXHR, textStatus, errorThrown) {
if (onError) {
onError(jqXHR, textStatus);
}
}
});
setTimeout(callService, 1000);
}
Request:
requests.push(
$.get(link.URL, function (data) {
if (IsCancel()) {
return;
}
var jdata = $($.parseHTML(data));
var parser = new ProductPageParser(jdata, link.URL);
if (!parser.isValidProduct()) {
console.log(link.URL + " is not a valid product, skipped.");
link.processed = true;
return;
}
// Process associated (linked) product on this page according to user preferences.
crawlLinkedProducts(jdata, link.URL, config);
// Store product into a collection.
var product = getProductForParser(parser, link);
//product.dbRawProductURL = urlRaw;
if (product) {
products.push(product);
}
link.processed = true;
})
);
And as I have parse in parser, here's second level parser. I parsed products on main page:
$(productUrls).each(function (index, link) {
if (!link.processed) {
console.log("Download second level -> " + link.URL);
requests_2level.push(
$.post(link.URL, "", function (data) {
if (IsCancel()) {
return;
}
console.log("End download second level -> " + link.URL);
var jdata = $($.parseHTML(data));
var parser = new ProductPageParser(jdata, link.URL);
if (!parser.isValidProduct()) {
console.log(link.URL + " is not a valid product, skipped.");
link.processed = true;
return;
}
var hackUrl = "//amazon.com/o/ASIN/" + parser.getAsin();
link.URL = hackUrl;
var product = getProductForParser(parser, link);
if (product) {
products.push(product);
}
link.processed = true;
})
);
}
});
Anyone have idea how to fix this problem?
If Amazon keep redirecting you from HTTPS to HTTP then there is nothing you can do about that short of:
Complaining hard enough at Amazon that they fix it or
Using a difference service
Decide whether to use http or https and use the same on for every call.

Image upload to server in node.js without using express

Below is my server code where I try to get the file that was uploaded. However, fs.writeFiledoesn't work, so I'm assuming that I'm doing something wrong.
server.on('request', function(request, response){
....
if((pathArray[1] == "photos") && (pathArray[2] = "new")){
var imagesPath = './images';
uploadPhoto(response, imagesPath);
}
else if(path == '/document/save'){
console.log("path: " + path);
var body = '';
request.on('data', function(data){
body += data;
});
request.on('end', function() {
var note = querystring.parse(body);
console.log("Body data: " + note);
var newPath = "./images/myimage.jpg";
fs.writeFile( newPath, body, function (err) {
if (err) throw err;
});
});
}
Here is my HTML for the form, if it helps anyone:
function uploadPhoto(response, imageLoc){
response.writeHead(200, {
'Content-Type': 'text/html'
});
response.write('<html><body>');
response.write('<div class="uploadFile">');
response.write('<link rel="stylesheet" type="text/css" href="style.css">');
response.write('<form action =/document/save>');
response.write('<method = "post">');
response.write('<enctype="multipart/form-data">');
response.write('<label for="name">Upload new photo</label>');
response.write('<br></br>');
response.write('<input type="file" name="name">');
response.write('<br></br>');
response.write('<button type="submit">Upload</button>');
response.write('</div>');
response.write('</body></html>');
response.write('</form>');
response.end();
}
After I upload the file, url goes to /document/save/uploadImage.jpg. But when I try to read the content of the image ("body") to save the image into a folder and then display it, seems that the content of the object of the request is empty.
How do I get the content of the image using node.js without express, or any other external libraries than what I have? Is fs.writeFile a good function to use when writing a binary file?
What has to be taken into consideration is the fact that the received data from the upload has this sort of format:
------WebKitFormBoundary9BhXe3lt2UddCDz9
Content-Disposition: form-data; name="document"; filename="globeSS.jpg"
Content-Type: image/jpeg
ÿØÿà JFIF d d ÿì Ducky d ÿá
//[binary binary binary...]
Ï[leñnœ“}ÛOyŠVÑ0êãXÂ}Ö'±”É iÉöÚ$GTQ7äŽø
uÚ_êÍòXgV¿Õ=€q`]a­KRÐÀ
ò<ÿÙ
------WebKitFormBoundary9BhXe3lt2UddCDz9--
To get the binary data only( and thus the file), the programmer has to figure out a way to clip the binary out of that data. In the below code, binary of the picture is all saved in memory, so if the user uploads a particularly large file, the following implementation might fail. It'd be best to try to write down the file in chucks.
request.setEncoding('binary');
//Grabbing all data from the image
var body = ''
var binaryEnd; //gets the string that indicates the location of the end of the binary file
var first = true;
request.on('data', function(data) {
if(first)
binaryEnd = data.toString().substring(0, data.toString().indexOf('\n')-1);
first = false;
body += data
});
//Dealing with the image once we have everything
request.on('end', function() {
var note = querystring.parse(body, '\r\n', ':')
console.log(note)
//making sure than an image was submitted
if (note['Content-Type'].indexOf("image") != -1)
{
//get the filename
var fileInfo = note['Content-Disposition'].split('; ');
for (value in fileInfo){
if (fileInfo[value].indexOf("filename=") != -1){
fileName = fileInfo[value].substring(10, fileInfo[value].length-1);
if (fileName.indexOf('\\') != -1)
fileName = fileName.substring(fileName.lastIndexOf('\\')+1);
console.log("My filename: " + fileName);
}
}
//Get the type of the image (eg. image/gif or image/png)
var entireData = body.toString();
var contentTypeRegex = /Content-Type: image\/.*/;
contentType = note['Content-Type'].substring(1);
//Get the location of the start of the binary file,
//which happens to be where contentType ends
var upperBoundary = entireData.indexOf(contentType) + contentType.length;
var shorterData = entireData.substring(upperBoundary);
//replace trailing and starting spaces
var binaryDataAlmost = shorterData.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
//Cut the extra things at the end of the data (Webkit stuff)
var binaryData = binaryDataAlmost.substring(0, binaryDataAlmost.indexOf(firstLine));
//Write to a file
fs.writeFile('./images/' + fileName , binaryData, 'binary', function(err)
{
//forward to another location after writing data
response.writeHead(302, {
'location':'/index.html'
});
response.end();
});
}
else
respond(404, "Please input an image", response);
});
This should work in all browsers (please note that internet explorer does not limit its data with ------WebkitFormBoundary, but something else (I think only -----, but I forgot.)

How to post image on twitter in Phonegap using javascript

I'm currently implementing a web smartphone application with Phonegap. On this application, users can post images they take with the phone camera on Facebook. This feature has been succesfully implemented only using javascript, by sending a base 64 encoded image. Now, I want to implement the same feature using Twitter.
I found some very interesting blog posts about this and I'm already be able to update the user status only using javascript... but I can't post images too using the update_with_media Twitter web service.
According too this post, someone says it's impossible to implement this operation without using a server side code (like a php script for example).
So my question is : is it possible to use the update_with_media Twitter web service only with javascript ?
I send you my code to have an overview of the current solution. I've taken this article as working base : http://oodlestechnologies.com/blogs/Twitter-integration-on-PhoneGap-using-ChildBrowser-and-OAuth-for-iOS-and-Android-Platforms
Here is my HTML code.
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<script type="text/javascript" src="../js/jquery/jquery.min.js"></script>
<script type="text/javascript" src="../cordova-2.5.0.js"></script>
<script type="text/javascript" src="../js/childBrowser/childbrowser.js"></script>
<script type="text/javascript" src="../js/helpers/jsOAuth-1.3.6.js"></script>
<script type="text/javascript" src="../js/helpers/twitter.js"></script>
</head>
<body>
<h4>Oodles Twitter App</h4>
<table border="1">
<tr>
<th>Login using Twitter</th>
<th>
<button id="loginBtn" onclick="Twitter.init();">Login</button>
<button id="logoutBtn" onclick="logOut();">Logout</button>
</th>
</tr>
<tr id="tweetText">
<td colspan="2"><textarea id="tweet"></textarea></td>
</tr>
<tr id="tweetBtn">
<td colspan="2" align="right">
<button id="tweeter" onclick="Twitter.tweet();">Tweet</button>
</td>
</tr>
<tr><td colspan="2"><div id="welcome">Please Login to use this app</div></td></tr>
</table>
<br/>
<br/>
<button onclick="javascript:location.reload();">Recharger la page</button>
</body>
</html>
Here is my twitter.js code : (The point is in the post method)
$(document).ready(function() {
document.addEventListener("deviceready", onDeviceReady, false);
});
function onDeviceReady() {
var root = this;
cb = window.plugins.childBrowser;
if (!localStorage.getItem(twitterKey)) {
$("#loginBtn").show();
$("#logoutBtn").hide();
$("tweetBtn").hide();
$("tweetText").hide();
}
else {
$("#loginBtn").hide();
$("#logoutBtn").show();
$("tweetBtn").show();
$("tweetText").show();
}
if (cb != null) {
cb.onLocationChange = function(loc) {
root.locChanged(loc);
};
cb.onClose = function() {
root.onCloseBrowser()
};
cb.onOpenExternal = function() {
root.onOpenExternal();
};
}
}
function onCloseBrowser() {
console.log("onCloseBrowser!");
}
function locChanged(loc) {
console.log("locChanged!");
}
function onOpenExternal() {
console.log("onOpenExternal!");
}
// Consumer key : ...
// Consumer secret : ...
// GLOBAL VARS
var oauth; // It Holds the oAuth data request
var requestParams; // Specific param related to request
var options = {consumerKey: '...', consumerSecret: '...', callbackUrl: "http://www.google.fr"};
var twitterKey = "twtrKey"; // This key is used for storing Information related
var Twitter = {
init: function() {
// Apps storedAccessData , Apps Data in Raw format
var storedAccessData, rawData = localStorage.getItem(twitterKey);
// here we are going to check whether the data about user is already with us.
if (localStorage.getItem(twitterKey) !== null) {
// when App already knows data
storedAccessData = JSON.parse(rawData); //JSON parsing
//options.accessTokenKey = storedAccessData.accessTokenKey; // data will be saved when user first time signin
options.accessTokenSecret = storedAccessData.accessTokenSecret; // data will be saved when user first first signin
// javascript OAuth take care of everything for app we need to provide just the options
oauth = OAuth(options);
oauth.get('https://api.twitter.com/1/account/verify_credentials.json?skip_status=true',
function(data) {
var entry = JSON.parse(data.text);
console.log("USERNAME: " + entry.screen_name);
}
);
}
else {
// we have no data for save user
oauth = OAuth(options);
oauth.get('https://api.twitter.com/oauth/request_token',
function(data) {
requestParams = data.text;
cb.showWebPage('https://api.twitter.com/oauth/authorize?' + data.text); // This opens the Twitter authorization / sign in page
cb.onLocationChange = function(loc) {
Twitter.success(loc);
}; // Here will will track the change in URL of ChildBrowser
},
function(data) {
console.log("ERROR: " + JSON.stringify(data));
}
);
}
},
/*
When ChildBrowser's URL changes we will track it here.
We will also be acknowledged was the request is a successful or unsuccessful
*/
success: function(loc) {
// Here the URL of supplied callback will Load
/*
Here Plugin will check whether the callback Url matches with the given Url
*/
if (loc.indexOf("http://www.google.fr") >= 0) {
// Parse the returned URL
var index, verifier = '';
var params = loc.substr(loc.indexOf('?') + 1);
params = params.split('&');
for (var i = 0; i < params.length; i++) {
var y = params[i].split('=');
if (y[0] === 'oauth_verifier') {
verifier = y[1];
}
}
// Here we are going to change token for request with token for access
/*
Once user has authorised us then we have to change the token for request with token of access
here we will give data to localStorage.
*/
oauth.get('https://api.twitter.com/oauth/access_token?oauth_verifier=' + verifier + '&' + requestParams,
function(data) {
var accessParams = {};
var qvars_tmp = data.text.split('&');
for (var i = 0; i < qvars_tmp.length; i++) {
var y = qvars_tmp[i].split('=');
accessParams[y[0]] = decodeURIComponent(y[1]);
}
$('#oauthStatus').html('<span style="color:green;">Success!</span>');
$('#stage-auth').hide();
$('#stage-data').show();
oauth.setAccessToken([accessParams.oauth_token, accessParams.oauth_token_secret]);
// Saving token of access in Local_Storage
var accessData = {};
accessData.accessTokenKey = accessParams.oauth_token;
accessData.accessTokenSecret = accessParams.oauth_token_secret;
// Configuring Apps LOCAL_STORAGE
console.log("TWITTER: Storing token key/secret in localStorage");
localStorage.setItem(twitterKey, JSON.stringify(accessData));
oauth.get('https://api.twitter.com/1/account/verify_credentials.json?skip_status=true',
function(data) {
var entry = JSON.parse(data.text);
console.log("TWITTER USER: " + entry.screen_name);
$("#welcome").show();
document.getElementById("welcome").innerHTML = "welcome " + entry.screen_name;
successfulLogin();
// Just for eg.
app.init();
},
function(data) {
console.log("ERROR: " + data);
}
);
// Now we have to close the child browser because everthing goes on track.
window.plugins.childBrowser.close();
},
function(data) {
console.log(data);
}
);
}
else {
// Just Empty
}
},
tweet: function() {
var storedAccessData, rawData = localStorage.getItem(twitterKey);
storedAccessData = JSON.parse(rawData); // Paring Json
options.accessTokenKey = storedAccessData.accessTokenKey; // it will be saved on first signin
options.accessTokenSecret = storedAccessData.accessTokenSecret; // it will be save on first login
// javascript OAuth will care of else for app we need to send only the options
oauth = OAuth(options);
oauth.get('https://api.twitter.com/1/account/verify_credentials.json?skip_status=true',
function(data) {
var entry = JSON.parse(data.text);
Twitter.post();
}
);
},
/*
We now have the data to tweet
*/
post: function() {
alert('Post !');
var theTweet = $("#tweet").val(); // You can change it with what else you likes.
oauth.post('https://upload.twitter.com/1/statuses/update_with_media.json',
{
'status': theTweet,
'media': //HERE IS THE PROBLEM, WHAT TO DO HERE ?
}, "multipart/form-data",
function(data)
{
alert('Data 1 !');
console.log('------Data1 : ' + data);
var entry = JSON.parse(data.text);
console.log(entry);
done();
},
function(data) {
//var json_result = JSON.parse(data);
//alert(json_result.text.error);
var entry = JSON.stringify(data);
console.log('------Data2 : ' + entry);
}
);
}
}
function done() {
alert("OKKK !");
$("#tweet").val('');
}
function successfulLogin() {
$("#loginBtn").hide();
$("#logoutBtn,#tweet,#tweeter,#tweetBtn,#tweetText").show();
}
function logOut() {
//localStorage.clear();
window.localStorage.removeItem(twitterKey);
document.getElementById("welcome").innerHTML = "Please Login to use this app";
$("#loginBtn").show();
$("#logoutBtn,#tweet,#tweeter,#tweetText,#tweetBtn").hide();
}
After many tests (sending a base64 image, sending a blob, sending a binary file, ...) here is the return message from Twitter I have :
{\"errors\":[{\"message\":\"Internal
error\",\"code\":131}]}","xml":"","requestHeaders":{"Content-Type":"multipart/form-data"},"responseHeaders":{"date":"Fri,
19 Apr 2013 15:45:28
GMT","content-encoding":"deflate","strict-transport-security":"max-age=631138519","status":"500
Internal Server
Error","server":"tfe","content-type":"application/json;
charset=utf-8","version":"HTTP/1.1"}}
A "solution" (by send a blob) have been posted on the Twitter dev forum but not working for me : dev.twitter.com/discussions/6969
Does anyone want to implement the same feature or have a solution ? Thank you !
------ EDITED :
I just want to use Javascript and I don't want to implement any server-side solution (no PHP, C#, Java...).
According to the docs, Twitter requires the multipart/form-data enctype, which means a base64 string isn't going to work.
Unlike POST statuses/update, this method expects raw multipart data. Your POST request's Content-Type should be set to multipart/form-data with the media[] parameter ~ https://dev.twitter.com/docs/api/1/post/statuses/update_with_media
However, you could host an endpoint that takes base64, converts it to a real file, and forwards the request to Twitter. For example (untested):
<?php
$base64 = $_POST['image'];
$data = base64_decode( $base64 );
// Make name unique to avoid conflicts.
$temp_file = uniqid() . $_POST['name'];
// Save the file to a temp location.
file_put_contents( $temp_file, $data );
$temp_info = pathinfo( $temp_file );
$temp_type = $temp_info['extension'];
$temp_name = basename( $temp_file, '.' . $temp_type );
// OAuth library recommended by Twitter: https://github.com/themattharris/tmhOAuth
// See original: https://github.com/themattharris/tmhOAuth-examples/blob/master/images.php
require 'tmhOAuth.php';
require 'tmhUtilities.php';
$tmhOAuth = new tmhOAuth( array(
'consumer_key' => $_POST['consumer_key'],
'consumer_secret' => $_POST['consumer_secret'],
'user_token' => $_POST['user_token'],
'user_secret' => $_POST['user_secret'],
));
// note the type and filename are set here as well
// Edit: Not sure if the `type` and `filename` params are necessary.
$params = array( 'media[]' => "#{$temp_file};type={$temp_type};filename={$temp_name}" );
$code = $tmhOAuth->request( 'POST', $tmhOAuth->url( '1/status/update_with_media' ),
$params,
true, // use auth
true // multipart
);
// Remove temp file.
unlink( $temp_file );
if ( $code == 200 ) {
tmhUtilities::pr( json_decode( $tmhOAuth->response['response'] ) );
}
tmhUtilities::pr( htmlentities( $tmhOAuth->response['response'] ) );
?>
And you might call it like:
$.ajax({
// You'll want to use https to protect the oauth info.
url: "https://mysite.com/proxy.php",
type: "POST",
data: {
image: "base64 data...",
name: "foo.png",
consumer_key: options.consumerKey,
consumer_secret: options.consumerSecret,
user_token: options.accessTokenKey,
user_secret: options.accessTokenSecret
},
success: function( data ) {
console.log( data );
}
});
For anyone trying to post images to Twitter using client JS, I was able to post to twitter using the solution by gary-buynary-co-za (https://github.com/bytespider/jsOAuth/pull/11) at the end of this forum. Pretty much ended up using Phonegap FileTransfer and FileTransferOptions objects for transferring image to twitter api, but used jsOAuth for preparing FileTransferOptions headers and signatures. The solution could definitely be cleaned up though.

Download and open PDF file using Ajax

I have an action class that generates a PDF. The contentType is set appropriately.
public class MyAction extends ActionSupport
{
public String execute() {
...
...
File report = signedPdfExporter.generateReport(xyzData, props);
inputStream = new FileInputStream(report);
contentDisposition = "attachment=\"" + report.getName() + "\"";
contentType = "application/pdf";
return SUCCESS;
}
}
I call this action through an Ajax call. I don't know the way to deliver this stream to browser. I tried a few things but nothing worked.
$.ajax({
type: "POST",
url: url,
data: wireIdList,
cache: false,
success: function(response)
{
alert('got response');
window.open(response);
},
error: function (XMLHttpRequest, textStatus, errorThrown)
{
alert('Error occurred while opening fax template'
+ getAjaxErrorString(textStatus, errorThrown));
}
});
The above gives the error:
Your browser sent a request that this server could not understand.
Here is how I got this working
$.ajax({
url: '<URL_TO_FILE>',
success: function(data) {
var blob=new Blob([data]);
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="<FILENAME_TO_SAVE_WITH_EXTENSION>";
link.click();
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Updated answer using download.js
$.ajax({
url: '<URL_TO_FILE>',
success: download.bind(true, "<FILENAME_TO_SAVE_WITH_EXTENSION>", "<FILE_MIME_TYPE>")
});
You don't necessarily need Ajax for this. Just an <a> link is enough if you set the content-disposition to attachment in the server side code. This way the parent page will just stay open, if that was your major concern (why would you unnecessarily have chosen Ajax for this otherwise?). Besides, there is no way to handle this nicely acynchronously. PDF is not character data. It's binary data. You can't do stuff like $(element).load(). You want to use completely new request for this. For that pdf is perfectly suitable.
To assist you more with the server side code, you'll need to tell more about the language used and post an excerpt of the code attempts.
I don't really think that any of the past answers spotted out the problem of the original poster. They all presume a GET request while the poster was trying to POST data and get a download in response.
In the course of searching for any better answer we found this jQuery Plugin for Requesting Ajax-like File Downloads (if link is broken sometime in the future, see the internet archive).
In its "heart" it creates a "temporary" HTML form containing the given data as input fields. This form is appended to the document and posted to the desired URL. Right after that the form is removed again:
jQuery('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
.appendTo('body').submit().remove()
Update Mayur's answer looks pretty promising and very simple in comparison to the jQuery plug-in I referred to.
This is how i solve this issue.
The answer of Jonathan Amend on this post helped me a lot.
The example below is simplified.
For more details, the above source code is able to download a file using a JQuery Ajax request (GET, POST, PUT etc). It, also, helps to upload parameters as JSON and to change the content type to application/json (my default).
The html source:
<form method="POST">
<input type="text" name="startDate"/>
<input type="text" name="endDate"/>
<input type="text" name="startDate"/>
<select name="reportTimeDetail">
<option value="1">1</option>
</select>
<button type="submit"> Submit</button>
</form>
A simple form with two input text, one select and a button element.
The javascript page source:
<script type="text/javascript" src="JQuery 1.11.0 link"></script>
<script type="text/javascript">
// File Download on form submition.
$(document).on("ready", function(){
$("form button").on("click", function (event) {
event.stopPropagation(); // Do not propagate the event.
// Create an object that will manage to download the file.
new AjaxDownloadFile({
url: "url that returns a file",
data: JSON.stringify($("form").serializeObject())
});
return false; // Do not submit the form.
});
});
</script>
A simple event on button click. It creates an AjaxDownloadFile object. The AjaxDownloadFile class source is below.
The AjaxDownloadFile class source:
var AjaxDownloadFile = function (configurationSettings) {
// Standard settings.
this.settings = {
// JQuery AJAX default attributes.
url: "",
type: "POST",
headers: {
"Content-Type": "application/json; charset=UTF-8"
},
data: {},
// Custom events.
onSuccessStart: function (response, status, xhr, self) {
},
onSuccessFinish: function (response, status, xhr, self, filename) {
},
onErrorOccured: function (response, status, xhr, self) {
}
};
this.download = function () {
var self = this;
$.ajax({
type: this.settings.type,
url: this.settings.url,
headers: this.settings.headers,
data: this.settings.data,
success: function (response, status, xhr) {
// Start custom event.
self.settings.onSuccessStart(response, status, xhr, self);
// Check if a filename is existing on the response headers.
var filename = "";
var disposition = xhr.getResponseHeader("Content-Disposition");
if (disposition && disposition.indexOf("attachment") !== -1) {
var filenameRegex = /filename[^;=\n]*=(([""]).*?\2|[^;\n]*)/;
var matches = filenameRegex.exec(disposition);
if (matches != null && matches[1])
filename = matches[1].replace(/[""]/g, "");
}
var type = xhr.getResponseHeader("Content-Type");
var blob = new Blob([response], {type: type});
if (typeof window.navigator.msSaveBlob !== "undefined") {
// IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed.
window.navigator.msSaveBlob(blob, filename);
} else {
var URL = window.URL || window.webkitURL;
var downloadUrl = URL.createObjectURL(blob);
if (filename) {
// Use HTML5 a[download] attribute to specify filename.
var a = document.createElement("a");
// Safari doesn"t support this yet.
if (typeof a.download === "undefined") {
window.location = downloadUrl;
} else {
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
}
} else {
window.location = downloadUrl;
}
setTimeout(function () {
URL.revokeObjectURL(downloadUrl);
}, 100); // Cleanup
}
// Final custom event.
self.settings.onSuccessFinish(response, status, xhr, self, filename);
},
error: function (response, status, xhr) {
// Custom event to handle the error.
self.settings.onErrorOccured(response, status, xhr, self);
}
});
};
// Constructor.
{
// Merge settings.
$.extend(this.settings, configurationSettings);
// Make the request.
this.download();
}
};
I created this class to added to my JS library. It is reusable. Hope that helps.
What worked for me is the following code, as the server function is retrieving File(memoryStream.GetBuffer(), "application/pdf", "fileName.pdf");:
$http.get( fullUrl, { responseType: 'arraybuffer' })
.success(function (response) {
var blob = new Blob([response], { type: 'application/pdf' });
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob); // for IE
}
else {
var fileURL = URL.createObjectURL(blob);
var newWin = window.open(fileURL);
newWin.focus();
newWin.reload();
}
});
To fix the blank PDF issue in post request to get stream data like PDF, we need to add response type as 'arraybuffer' or 'blob' in request
$.ajax({
url: '<URL>',
type: "POST",
dataType: 'arraybuffer',
success: function(data) {
let blob = new Blob([data], {type: 'arraybuffer'});
let link = document.createElement('a');
let objectURL = window.URL.createObjectURL(blob);
link.href = objectURL;
link.target = '_self';
link.download = "fileName.pdf";
(document.body || document.documentElement).appendChild(link);
link.click();
setTimeout(()=>{
window.URL.revokeObjectURL(objectURL);
link.remove();
}, 100);
}
});
You could use this plugin which creates a form, and submits it, then removes it from the page.
jQuery.download = function(url, data, method) {
//url and data options required
if (url && data) {
//data can be string of parameters or array/object
data = typeof data == 'string' ? data : jQuery.param(data);
//split params into form inputs
var inputs = '';
jQuery.each(data.split('&'), function() {
var pair = this.split('=');
inputs += '<input type="hidden" name="' + pair[0] +
'" value="' + pair[1] + '" />';
});
//send request
jQuery('<form action="' + url +
'" method="' + (method || 'post') + '">' + inputs + '</form>')
.appendTo('body').submit().remove();
};
};
$.download(
'/export.php',
'filename=mySpreadsheet&format=xls&content=' + spreadsheetData
);
This worked for me. Found this plugin here
Concerning the answer given by Mayur Padshala this is the correct logic to download a pdf file via ajax but as others report in the comments this solution is indeed downloads a blank pdf.
The reason for this is explained in the accepted answer of this question: jQuery has some issues loading binary data using AJAX requests, as it does not yet implement some HTML5 XHR v2 capabilities, see this enhancement request and this discussion.
So using HTMLHTTPRequest the code should look like this:
var req = new XMLHttpRequest();
req.open("POST", "URL", true);
req.responseType = "blob";
req.onload = function (event) {
var blob = req.response;
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="name_for_the_file_to_save_with_extention";
link.click();
}
The following code worked for me
//Parameter to be passed
var data = 'reportid=R3823&isSQL=1&filter=[]';
var xhr = new XMLHttpRequest();
xhr.open("POST", "Reporting.jsp"); //url.It can pdf file path
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.responseType = "blob";
xhr.onload = function () {
if (this.status === 200) {
var blob = new Blob([xhr.response]);
const url = window.URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = 'myFile.pdf';
a.click();
setTimeout(function () {
// For Firefox it is necessary to delay revoking the ObjectURL
window.URL.revokeObjectURL(data)
, 100
})
}
};
xhr.send(data);
Hope this will save you a few hours and spare you from a headache.
It took me a while to figure this out, but doing regular $.ajax() request ruined my PDF file, while requesting it through address bar worked perfectly.
Solution was this:
Include download.js: http://danml.com/download.html
Then use XMLHttpRequest instead of $.ajax() request.
var ajax = new XMLHttpRequest();
ajax.open("GET", '/Admin/GetPdf' + id, true);
ajax.onreadystatechange = function(data) {
if (this.readyState == 4)
{
if (this.status == 200)
{
download(this.response, "report.pdf", "application/pdf");
}
else if (this.responseText != "")
{
alert(this.responseText);
}
}
else if (this.readyState == 2)
{
if (this.status == 200)
{
this.responseType = "blob";
}
else
{
this.responseType = "text";
}
}
};
ajax.send(null);
create a hidden iframe, then in your ajax code above:
url: document.getElementById('myiframeid').src = your_server_side_url,
and remove the window.open(response);
This snippet is for angular js users which will face the same problem, Note that the response file is downloaded using a programmed click event.
In this case , the headers were sent by server containing filename and content/type.
$http({
method: 'POST',
url: 'DownloadAttachment_URL',
data: { 'fileRef': 'filename.pdf' }, //I'm sending filename as a param
headers: { 'Authorization': $localStorage.jwt === undefined ? jwt : $localStorage.jwt },
responseType: 'arraybuffer',
}).success(function (data, status, headers, config) {
headers = headers();
var filename = headers['x-filename'];
var contentType = headers['content-type'];
var linkElement = document.createElement('a');
try {
var blob = new Blob([data], { type: contentType });
var url = window.URL.createObjectURL(blob);
linkElement.setAttribute('href', url);
linkElement.setAttribute("download", filename);
var clickEvent = new MouseEvent("click", {
"view": window,
"bubbles": true,
"cancelable": false
});
linkElement.dispatchEvent(clickEvent);
} catch (ex) {
console.log(ex);
}
}).error(function (data, status, headers, config) {
}).finally(function () {
});
I have found a solution that solved this problem for me (blank pdf when using jquery ajax). I've found this magical solution here: https://www.py4u.net/discuss/904599 (Answer 2) and it involves adding xhrFields to your ajax call:
xhrFields: {
responseType: 'blob'
}
My working example:
$.ajax({
url: "myUrl",
type: 'GET',
headers: {"token": mySecurityToken},
xhrFields: {
responseType: 'blob'
},
data: {id: myId}
}).done(function( data, statusText, xhr ) {
var filename = "";
var disposition = xhr.getResponseHeader("Content-Disposition");
if (disposition && (disposition.indexOf("attachment") !== -1) || disposition.indexOf("filename") !== -1) {
var filenameRegex = /filename[^;=\n]*=(([""]).*?\2|[^;\n]*)/;
var matches = filenameRegex.exec(disposition);
if (matches != null && matches[1])
filename = matches[1].replace(/[""]/g, "");
}
var type = xhr.getResponseHeader("Content-Type");
var blob = new Blob([data], {type: type});
if (typeof window.navigator.msSaveBlob !== "undefined") {
// IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed.
window.navigator.msSaveBlob(blob, filename);
} else {
var URL = window.URL || window.webkitURL;
var downloadUrl = URL.createObjectURL(blob);
if (filename) {
// Use HTML5 a[download] attribute to specify filename.
var a = document.createElement("a");
// Safari doesn"t support this yet.
if (typeof a.download === "undefined") {
window.location = downloadUrl;
} else {
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
}
} else {
window.location = downloadUrl;
}
setTimeout(function () {
URL.revokeObjectURL(downloadUrl);
}, 100); // Cleanup
}
})
I hope this will solve this nasty issue for many of you.
var xhr;
var beforeSend = function(){
$('#pleasewaitDL').modal('show');
}
$(function () {
$('#print_brochure_link').click(function(){
beforeSend();
xhr = new XMLHttpRequest();
xhr.open("GET",$('#preparedPrintModalForm').attr('action'), true);
xhr.responseType = "blob";
xhr.onload = function (e) {
if (this.status === 200) {
var file = window.URL.createObjectURL(this.response);
var a = document.createElement("a");
a.href = file;
a.download = this.response.name || "Property Brochure";
console.log(file);
document.body.appendChild(a);
a.click();
window.onfocus = function () {
document.body.removeChild(a)
}
$('#pleasewaitDL').modal('hide');
};
};
xhr.send($('#preparedPrintModalForm').serialize());
});
$('#pleasewaitDLCancel').click(function() {
xhr.abort();
});
});
If you have to work with file-stream (so no physically saved PDF) like we do and you want to download the PDF without page-reload, the following function works for us:
HTML
<div id="download-helper-hidden-container" style="display:none">
<form id="download-helper-form" target="pdf-download-output" method="post">
<input type="hidden" name="downloadHelperTransferData" id="downloadHelperTransferData" />
</form>
<iframe id="pdf-helper-output" name="pdf-download-output"></iframe>
</div>
Javascript
var form = document.getElementById('download-helper-form');
$("#downloadHelperTransferData").val(transferData);
form.action = "ServerSideFunctionWhichWritesPdfBytesToResponse";
form.submit();
Due to the target="pdf-download-output", the response is written into the iframe and therefore no page reload is executed, but the pdf-response-stream is output in the browser as a download.
100% OK for all file types
// download the file
var link = document.createElement('a'),
filename = fname;
link.href = URL.createObjectURL(data);
link.download = filename;
link.click();
Do you have to do it with Ajax? Couldn't it be a possibility to load it in an iframe?
The best usage is to do an anchor or a form with the provided link, but it you need to do a validation or in other cases using jquery the best usage is to add a form and submit it using jquery (don't forget to set your request disposition as attachement on server side).
<form id="pdf-form" action="/link_to/download_your.pdf" accept-charset="UTF-8" method="get">
<input type="hidden" name="data" id="data" value="your data"></form>
and
Download my Pdf
then in jquery
$('#pdf').click(function () {
// your data if it json do it like this JSON.stringify(your_data_as_json)
$('#data').val(data);
$('#pdf-form').submit();
})

Categories