I am able to upload an image just fine. However, I need to send a json object with the image so that I can identify what record the image belongs to. What am I doing wrong? Why does the variable info come through as null on the java side of the service?
Client:
let file = event.target.files[0];
let info = {formId:8, formVersionId:2, formIndex:0};
var formData = new FormData();
formData.append('file', file);
formData.append('info', info );
$.ajax({
url: URL.BUILDER_URL + '/megaUpload',
type: 'POST',
data : formData,
cache : false,
contentType : false,
processData : false,
});
server:
public Response uploadFile( #FormDataParam("file") InputStream uploadedInputStream,
#FormDataParam("file") FormDataContentDisposition fileDetail,
#FormDataParam("info") GuiCreateResponse info) {
}
https://developer.mozilla.org/en-US/docs/Web/API/FormData/append
Here it clearly states that ,
FormData value can either be a string or a blob.
let file = event.target.files[0];
var formData = new FormData();
formData.append('file', file);
formData.append('formId', '8' );
formData.append('formVersionId', '2' );
formData.append('formIndex', '0');
This should solve your issue.
You are using formId, but you likely want to surround it in quotes as "formId". Try this code.
let file = event.target.files[0];
let info = {"formId":8, "formVersionId":2, "formIndex":0}; //See changes here
var formData = new FormData();
formData.append('file', file);
formData.append('info', info );
$.ajax({
url: URL.BUILDER_URL + '/megaUpload',
type: 'POST',
data : formData,
cache : false,
contentType : false,
processData : false,
});
Maybe this is your issue. The docs say, "When using the append() method it is possible to use the third optional parameter to pass a filename inside the Content-Disposition header that is sent to the server. When no filename is specified (or the parameter isn't supported), the name "blob" is used."
Since you do not provide a third parameter, I'm thinking the FormDataContentDisposition needs to look for "blob". Try changing your server annotations to check for that name:
public Response uploadFile( #FormDataParam("file") InputStream uploadedInputStream,
#FormDataParam("blob") FormDataContentDisposition fileDetail,
#FormDataParam("info") GuiCreateResponse info)
Related
I have tried two methods of sending the file and json data from client side over to server side. I been partially successful with both methods, I could use some help on completing this. I have two methods:
Method 1: I am able to access the file server side, but my form data (input, select, textarea) are not available to access.
Method 2: Appends the data to the formData object. The issue with method one is that I got it to work the other day, but I believe my browser cached the code and now its not working, so I feel like im back at step one. I am able to access the data by using request.data, but its bytes. The code that was working (request.form['json_data'] or request.files['file'].read()) and now its not working.
Client Side Code:
$('form[name="upload-form"]').on('submit', (event) => {
event.preventDefault();
$('#form-loader').fadeIn()
// Method 1
// let formData = new FormData($('#upload-form')[0]);
// Method 2
let entry = get_form_entry('upload-form'); -> UDF to get all form data. Iterates each form data and applies .val() and creates a dict.
const formData = new FormData();
formData.append('json_data', JSON.stringify(entry));
formData.append('file', $('#file')[0].files[0]);
$.ajax({
data: formData,
url: '/newEntry',
type: 'POST',
contentType: false,
processData: false,
cache: false,
success: function () {
$('#form-loader').fadeOut()
},
error: function () {
$('#form-loader').fadeOut()
},
})
})
Server Side Code:
json_data = request.form['json_data']
# json_data = request.data
file_data = request.files['file'].read()
I have a C# Rest API method which accepts System.IO Stream object as argument and I am trying to pass a file to this C# method as a file stream using Ajax call. Please suggest how to do this? I tried two ways-
Creating FormData object in JavaScript and
Creating File object in JavaScript.
C# method
[WebInvoke(UriTemplate = "/{businessObjectId}/uploadAttachment?filename={filename}&caption={caption}")]
public Message UploadAttachment(string filename, string caption, Stream stream, string businessObjectId){....}
Ajax Method
$("input[name*='btnUpload']").live("click", function () {
addGlobalAttachment();
});
function addGlobalAttachment() {
var fileData = $("input[id*='fileUpload']");
var fileName = fileData[0].files[0].name;
var fileCaption = $("input[name*='txtFileCaption']").val();
var friendlyID = $(".order").html();
var fileInput = new File(fileData[0].files, fileName, { type: "text/plain", lastModified: Date(0) });
var URL = "http://localhost:62059/Order/" + friendlyID + "/uploadAttachment?filename=" + fileName + "&caption=" + fileCaption;
$.ajax({
type: 'POST',
url: URL,
data: fileInput,
processData: false,
contentType: 'application/octet-stream',
success: function (attachmentResult) {
debugger;
}
},
error: function (result) {
}
});
}
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);
});
}
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,
...
};
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);
}
});