Corrupted files when uploading to Dropbox via Ajax - javascript

I'm trying to use the Dropbox API to send files to a specific Dropbox folder via a web interface using Ajax.
Here is my code:
function UploadFile(token, callback) {
var fileName = $('input[type=file]')[0].files[0].name,
fileData = new FormData($('#file-upload')[0]),
dbxHeaderParams = {
'path': '/' + fileName,
'mode': { '.tag': 'add' },
'autorename': true
};
$.ajax({
url: 'https://content.dropboxapi.com/2/files/upload',
type: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/octet-stream',
'Dropbox-API-Arg': JSON.stringify(dbxHeaderParams)
},
data: fileData,
processData: false,
contentType: false,
success: function(result) {
console.log('NICE BRO');
callback();
},
error: function(error) {
console.error(error);
callback();
}
});
}
This code works: files are uploaded to my Dropbox folder and i can open them. They even have the right name and (almost) the right size. But the problem is that they are all corrupted because some lines are added during the process.
Here is an example: if I want to upload a .txt file containing this:
harder better faster stronger
Once uploaded on my Dropbox, it will looks like this:
-----------------------------2308927457834
Content-Disposition: form-data; name="file"; filename="test.txt"
Content-Type: text/plain
harder better faster stronger
-----------------------------2308927457834--
I assume this is why I can't open files like images. I've tried several solutions but none of them can solve this. What am I doing wrong ? Thanks !

Seeing the relevant pieces of the HTML would be useful, but it looks like the issue is that you're supplying a FormData object to the upload call, as opposed to the File.
Try replacing this line:
fileData = new FormData($('#file-upload')[0]),
with this:
fileData = $('input[type=file]')[0].files[0],

Related

How to show AJAX response data on a website

I'm writting a Spring Boot applicaiton in which I have a website with a submenu with several computer games. When I click on an position in this submenu, I want server to send an image (by image I mean a path to the image) of this game as a response, and after the response comes back to my JS on a website, I want to show it on the website. What I have already done is sending a request to server, and selecting an image based on request data. I don't know how to send a response and use it on my website.
Here is my code:
Java:
#RequestMapping("/change-game")
public String changeGame(HttpServletRequest request, #RequestBody GameData data){
File file;
String game = data.getName();
switch (game) {
//some code which actually works. I removed it to save space
}
request.setAttribute("gameIcon", file);
return "index";
}
JavaScript:
$("#selectGameSubmenu li").click(function(e){
e.preventDefault();
var option = $(this).data("option");
console.log(option + " " + JSON.stringify({"option": option}));
$.ajax({
type: "POST",
url: "http://localhost:8080/change-game",
data: JSON.stringify({name: option}),
contentType: "application/json; charset=utf-8",
dataType: "json"
});
});
HTML:
<img src="${gameIcon}" alt="${altGameIcon}"
style="width:100px;height:100px" class="gameLogoCenter"/>
I would add a new method that returns only the image path for your AJAX calls to consume.
For example
#ResponseBody
#PostMapping("/change-game-icon")
public String changeGameIcon(#RequestBody GameData data) {
File file;
String game = data.getName();
switch (game) {
//some code which actually works. I removed it to save space
}
return file.toString();
}
and in your JS
$.ajax({
url: '/change-game-icon',
method: 'post', // or "type" if your jQuery is really old
data: JSON.stringify({name: option}),
dataType: 'text',
contentType: 'application/json'
}).done(iconPath => {
$('img.gameLogoCenter').prop('src', iconPath)
})

Upload form data fails with axios

I have a form with multiple fileds, which one is a file input. I use axios to upload the file under a separate attribute:
axios.post(ENDPOINT,{
form: formData,
image: image
}, getAuthorizationHeader())
function getAuthorizationHeader() {
return {
headers: {
'Authorization': //...,
'Content-Type': undefined
}
};
}
formData is created like this:
let formData = new FormData();
formData.append('title', values.title);
formData.append('description', values.description);
formData.append('amount', values.amount);
And the image is:
Under the network tab of the Chrome Dev tool, When I look at the request, it looks like this:
As you can see in the screenshot, the file is empty? The CONTENT-TYPE is application/json which is not what I expected. I expected browser to detect the CONTENT-TYPE as multipart/form-data
What is wrong here?
First of all, image should be part of the formData:
formData.append('image', <stream-of-the-image>, 'test.png')
Secondly, formData should be the second parameter of axios.post:
axios.post(ENDPOINT, formData, getAuthorizationHeader())
Last but no least, you should merge formData.getHeaders():
function getAuthorizationHeader() {
return {
headers: Object.assign({
'Authorization': //...,
}, formData.getHeaders())
};
}
Sample code for your reference: https://github.com/tylerlong/ringcentral-js-concise/blob/master/test/fax.spec.js

Upload to Image Server using jQuery as Relay

Problem:
I have a situation where I'd like to upload a file (pdf, image, etc.) to an API Endpoint that accepts one of these types of files. However, the file is located on another web service somewhere. I'm trying to devise a clever solution that will allow me to (a) download the remote file (and store it as bytes in memory or something) then (b) upload that file through the API.
I have jQuery code that demonstrates how to upload a local file using jQuery with no backend code, but I'd like to extend it to allow me to upload something that is stored remotely.
Constraints:
I don't want to use any backend infrastructure on my image uploading page (ie. no php, python, ruby, etc.)
I don't want the end user of my form to need to download the file to their machine and upload the file as a two-step process.
What I've got so far:
I've seen some solutions on SO that kind-of connect the dots here in terms of downloading a file as a bytearray, but nothing that demonstrates how you might upload that.
Download File from Bytes in JavaScript
jQuery-only File Upload to Stripe API*
Keep in mind, Stripe is the example I have, but I'd like to try and replicate this on say Imgur or another API (if I can get this working). Hopefully someone else has some ideas!
$('#fileinfo').submit(function(event) {
event.preventDefault();
var data = new FormData();
var publishableKey = 'pk_test_***';
data.append('file', $('#file-box')[0].files[0]);
data.append('purpose', 'identity_document');
$.ajax({
url: 'https://uploads.stripe.com/v1/files',
data: data,
headers: {
'Authorization': 'Bearer ' + publishableKey,
// 'Stripe-Account': 'acct_STRIPE-ACCOUNT-ID'
},
cache: false,
contentType: false,
processData: false,
type: 'POST',
}).done(function(data) {
$('#label-results').text('Success!');
$('#upload-results').text(JSON.stringify(data, null, 3));
}).fail(function(response, type, message) {
$('#label-results').text('Failure: ' + type + ', ' + message);
$('#upload-results').text(JSON.stringify(response.responseJSON, null, 3));
});
return false;
});
I actually got this working for Stripe by doing this:
https://jsfiddle.net/andrewnelder/up59zght/
var publishableKey = "pk_test_xxx"; // Platform Publishable Key
var stripeAccount = "acct_xxx"; // Connected Account ID
$(document).ready(function () {
$('#file-upload').on('submit', function (e) {
e.preventDefault();
console.log('Clicked!');
var route = $('#file-route').val(); // URL OF FILE
var fname = route.split("/").slice(-1)[0].split("?")[0];
var blob = fetchBlob(route, fname, uploadBlob);
});
});
function fetchBlob(route, fname, uploadBlob) {
console.log('Fetching...')
var oReq = new XMLHttpRequest();
oReq.open("GET", route, true);
oReq.responseType = "blob";
oReq.onload = function(e) {
var blob = oReq.response;
console.log('Fetched!')
uploadBlob(fname, blob);
};
oReq.send();
}
function uploadBlob(fname, blob) {
var fd = new FormData();
fd.append('file', blob);
fd.append('purpose', 'identity_document');
console.log('Uploading...');
$.ajax({
url: 'https://uploads.stripe.com/v1/files',
data: fd,
headers: {
'Authorization': 'Bearer ' + publishableKey,
'Stripe-Account': stripeAccount
},
cache: false,
contentType: false,
processData: false,
type: 'POST',
}).done(function(data) {
console.log('Uploaded!')
}).fail(function(response, type, message) {
console.log(message);
});
}

Inconsistent file contents on uploading through Dropbox API

I am using the Dropbox Core API to upload and download files via a chrome extension. When I upload text files such as with extensions .txt, .js, .json or .c the files get uploaded successfully but when I upload files with extensions .pdf, .jpg etc (media files) then the contents are disfigured or absent, though the file size is non-zero, sometimes even larger than the original file. This clearly means that the data that is read is being written as well but I guess there is some problem with the way I am reading or writing the data. The code is posted below for reference.
$(document).on("click", "#id_submit",uploadProcess);
function uploadProcess()
{
var file = $("#upload_file")[0].files[0];
console.log(file);
if (!file){
alert ("No file selected to upload.");
return false;
}
var reader = new FileReader();
//reader.readAsText(file, "UTF-8");
reader.readAsBinaryString(file);
reader.onload = function (evt) {
uploadFile(file.name, evt.target.result, file.size, file.type);
//console.log(evt.target.result);
var control = $("#upload_file");
control.replaceWith( control = control.clone( true ));
}
}
//function to upload file to folder
function uploadFile(filepath,data,contentLength,contentType){
var url = "https://api-content.dropbox.com/1/files_put/auto/"+filepath;
var headers = {
Authorization: 'Bearer ' + getAccessToken(),
contentLength: contentLength
}
var args = {
url: url,
headers: headers,
crossDomain: true,
crossOrigin: true,
type: 'PUT',
contentType: contentType,
data : data,
dataType: 'json',
success: function(data)
{
console.log(data);
},
error: function(jqXHR)
{
console.log(jqXHR);
}
};
$.ajax(args);
}
You can actually pass a file in your ajax request(depending on browser support). Just pass the files in your args object, also you need to set processDate and contentType to false to prevent $.ajax from manipulating the file object
var args = {
...
contentType: false,
data : file,
processData: false,
...
};

How to set a header for a HTTP GET request, and trigger file download?

Update 20140702:
The solution
Detailed answer as a blog post
(but I'm marking one of the other answers as accepted instead of my own,
as it got me halfway there, and to reward the effort)
It appears that setting a HTTP request header is not possible through links with <a href="...">, and can only be done using XMLHttpRequest.
However, the URL linked to is a file that should be downloaded (browser should not navigate to its URL), and I am not sure is this can be done using AJAX.
Additionally, the file being returned is a binary file, and AJAX is not intended for that.
How would one go about triggering a file download with a HTTP request that has a custom header added to it?
edit: fix broken link
There are two ways to download a file where the HTTP request requires that a header be set.
The credit for the first goes to #guest271314, and credit for the second goes to #dandavis.
The first method is to use the HTML5 File API to create a temporary local file,
and the second is to use base64 encoding in conjunction with a data URI.
The solution I used in my project uses the base64 encoding approach for small files,
or when the File API is not available,
otherwise using the the File API approach.
Solution:
var id = 123;
var req = ic.ajax.raw({
type: 'GET',
url: '/api/dowloads/'+id,
beforeSend: function (request) {
request.setRequestHeader('token', 'token for '+id);
},
processData: false
});
var maxSizeForBase64 = 1048576; //1024 * 1024
req.then(
function resolve(result) {
var str = result.response;
var anchor = $('.vcard-hyperlink');
var windowUrl = window.URL || window.webkitURL;
if (str.length > maxSizeForBase64 && typeof windowUrl.createObjectURL === 'function') {
var blob = new Blob([result.response], { type: 'text/bin' });
var url = windowUrl.createObjectURL(blob);
anchor.prop('href', url);
anchor.prop('download', id+'.bin');
anchor.get(0).click();
windowUrl.revokeObjectURL(url);
}
else {
//use base64 encoding when less than set limit or file API is not available
anchor.attr({
href: 'data:text/plain;base64,'+FormatUtils.utf8toBase64(result.response),
download: id+'.bin',
});
anchor.get(0).click();
}
}.bind(this),
function reject(err) {
console.log(err);
}
);
Note that I'm not using a raw XMLHttpRequest,
and instead using ic-ajax,
and should be quite similar to a jQuery.ajax solution.
Note also that you should substitute text/bin and .bin with whatever corresponds to the file type being downloaded.
The implementation of FormatUtils.utf8toBase64
can be found here
Try
html
<!-- placeholder ,
`click` download , `.remove()` options ,
at js callback , following js
-->
<a>download</a>
js
$(document).ready(function () {
$.ajax({
// `url`
url: '/echo/json/',
type: "POST",
dataType: 'json',
// `file`, data-uri, base64
data: {
json: JSON.stringify({
"file": "data:text/plain;base64,YWJj"
})
},
// `custom header`
headers: {
"x-custom-header": 123
},
beforeSend: function (jqxhr) {
console.log(this.headers);
alert("custom headers" + JSON.stringify(this.headers));
},
success: function (data) {
// `file download`
$("a")
.attr({
"href": data.file,
"download": "file.txt"
})
.html($("a").attr("download"))
.get(0).click();
console.log(JSON.parse(JSON.stringify(data)));
},
error: function (jqxhr, textStatus, errorThrown) {
console.log(textStatus, errorThrown)
}
});
});
jsfiddle http://jsfiddle.net/guest271314/SJYy3/
I'm adding another option. The answers above were very useful for me, but I wanted to use jQuery instead of ic-ajax (it seems to have a dependency with Ember when I tried to install through bower). Keep in mind that this solution only works on modern browsers.
In order to implement this on jQuery I used jQuery BinaryTransport. This is a nice plugin to read AJAX responses in binary format.
Then you can do this to download the file and send the headers:
$.ajax({
url: url,
type: 'GET',
dataType: 'binary',
headers: headers,
processData: false,
success: function(blob) {
var windowUrl = window.URL || window.webkitURL;
var url = windowUrl.createObjectURL(blob);
anchor.prop('href', url);
anchor.prop('download', fileName);
anchor.get(0).click();
windowUrl.revokeObjectURL(url);
}
});
The vars in the above script mean:
url: the URL of the file
headers: a Javascript object with the headers to send
fileName: the filename the user will see when downloading the file
anchor: it is a DOM element that is needed to simulate the download that must be wrapped with jQuery in this case. For example $('a.download-link').
i want to post my solution here which was done AngularJS, ASP.NET MVC. The code illustrates how to download file with authentication.
WebApi method along with helper class:
[RoutePrefix("filess")]
class FileController: ApiController
{
[HttpGet]
[Route("download-file")]
[Authorize(Roles = "admin")]
public HttpResponseMessage DownloadDocument([FromUri] int fileId)
{
var file = "someFile.docx"// asking storage service to get file path with id
return Request.ReturnFile(file);
}
}
static class DownloadFIleFromServerHelper
{
public static HttpResponseMessage ReturnFile(this HttpRequestMessage request, string file)
{
var result = request.CreateResponse(HttpStatusCode.OK);
result.Content = new StreamContent(new FileStream(file, FileMode.Open, FileAccess.Read));
result.Content.Headers.Add("x-filename", Path.GetFileName(file)); // letters of header names will be lowercased anyway in JS.
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = Path.GetFileName(file)
};
return result;
}
}
Web.config file changes to allow sending file name in custom header.
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Methods" value="POST,GET,PUT,PATCH,DELETE,OPTIONS" />
<add name="Access-Control-Allow-Headers" value="Authorization,Content-Type,x-filename" />
<add name="Access-Control-Expose-Headers" value="Authorization,Content-Type,x-filename" />
<add name="Access-Control-Allow-Origin" value="*" />
Angular JS Service Part:
function proposalService($http, $cookies, config, FileSaver) {
return {
downloadDocument: downloadDocument
};
function downloadFile(documentId, errorCallback) {
$http({
url: config.apiUrl + "files/download-file?documentId=" + documentId,
method: "GET",
headers: {
"Content-type": "application/json; charset=utf-8",
"Authorization": "Bearer " + $cookies.get("api_key")
},
responseType: "arraybuffer"
})
.success( function(data, status, headers) {
var filename = headers()['x-filename'];
var blob = new Blob([data], { type: "application/octet-binary" });
FileSaver.saveAs(blob, filename);
})
.error(function(data, status) {
console.log("Request failed with status: " + status);
errorCallback(data, status);
});
};
};
Module dependency for FileUpload: angular-file-download (gulp install angular-file-download --save). Registration looks like below.
var app = angular.module('cool',
[
...
require('angular-file-saver'),
])
. // other staff.
Pure jQuery.
$.ajax({
type: "GET",
url: "https://example.com/file",
headers: {
'Authorization': 'Bearer eyJraWQiFUDA.......TZxX1MGDGyg'
},
xhrFields: {
responseType: 'blob'
},
success: function (blob) {
var windowUrl = window.URL || window.webkitURL;
var url = windowUrl.createObjectURL(blob);
var anchor = document.createElement('a');
anchor.href = url;
anchor.download = 'filename.zip';
anchor.click();
anchor.parentNode.removeChild(anchor);
windowUrl.revokeObjectURL(url);
},
error: function (error) {
console.log(error);
}
});

Categories