I am trying to download multiple images as a zip file. As I am using Azure blob, first I listed all blobs then compressed it using Archiver and used pipe function to send it to the client. But I am getting zip as a raw file and it is not downloading. I am using Node js + Express.
Server-side script:
function zipURLs(urls, outStream) {
var zipArchive = archiver.create('zip');
async.eachLimit(urls, 3, function(url, done) {
console.log(url);
var stream = request.get(url);
stream.on('error', function(err) {
return done(err);
}).on('end', function() {
return done();
});
// Use the last part of the URL as a filename within the ZIP archive.
zipArchive.append(stream, { name : url.replace(/^.*\//, '') });
}, function(err) {
if (err) throw err;
zipArchive.finalize();
zipArchive.pipe(outStream);
});
}
var data = {};
data.blob_name = value;
console.log('downloading');
$.ajax({
type: 'POST',
data: JSON.stringify(data),
contentType: 'application/json',
url: 'download/',
success: function(data) { console.log(data); }
Outstream is res. So I get data like this:-
How can I download directly as zip file using js?
Thanks
There is a lot that goes into using ajax to download a file, first you have to be able to access the data in binary(not text which is the default) by setting the responseType to blob.
Then you have to use actually get a way to get the download dialog to show up, below you can see the anchor with download attribute technique.
jQuery.ajax({
url:'download/',
type:'POST',
data: JSON.stringify(data),
contentType: 'application/json',
xhrFields:{
responseType: 'blob'
},
success: function(data){
var anchor = document.getElementById('a');
var url = window.URL || window.webkitURL;
anchor.href = url.createObjectURL(data);
anchor.download = 'archive.zip';
document.body.append(anchor);
anchor.click();
setTimeout(function(){
document.body.removeChild(anchor);
url.revokeObjectURL(anchor.href);
}, 1};
},
error:function(){
}
});
requires jQuery3+
Related
In python I send files like this:
with open('D:\\someimage.jpg', 'rb') as image:
imager = image.read()
files = {'image': imager}
r = requests.post(url, files=files)
I want to do similar thing in js, but I always get 400 Bad Request error. The problem, I assume, is that I don't know what type the file should have. I've tried passing it in initial 'file' type, as array buffer, as binary string - nothing works. Here is my code:
var reader = new FileReader();
reader.readAsArrayBuffer(aFiles[0]);
reader.onload = function () {
var arrayBuffer = this.result,
array = new Uint8Array(arrayBuffer),
binaryString = String.fromCharCode.apply(null, array);
jQuery.ajax({
url: '/streamer',
method: 'POST',
files: {'image': binaryString},
success: function(response) {
alert(response);
},
error: function(xhr, status, error) {
alert(JSON.parse(xhr.responseText));
}});
You can send the file as a FormData (multipart/form-data) if you need to send more fields or as a Blob if you just want to send the binary directly.
jQuery tries to be smart about processing the data you send. But jQuery don't understand FormData or blobs, it sees it as an object and do the wrong thing. that's why you need to set processData = false and don't set the wrong request content-type by doing contentType = false and just let the xhr do that itself.
var fd = new FormData();
fd.append('image', aFiles[0] /*, optional filename */)
var req = jQuery.ajax({
url: '/streamer',
method: 'POST',
data: fd, // sends fields with filename mimetype etc
// data: aFiles[0], // optional just sends the binary
processData: false, // don't let jquery process the data
contentType: false // let xhr set the content type
});
// jQuery is promise A++ compatible and is the todays norms of doing things
req.then(function(response) {
console.log(response)
}, function(xhr) {
console.error('failed to fetch xhr', xhr)
})
But you don't really need jQuery for this things if you only support the latest browser that has the fetch api
var req = fetch('/streamer', {
method: 'post',
body: fd /* or aFile[0]*/
}); // returns a promise
req.then(function(response) {
// returns status + response headers
// but not yet the body,
// for that call `response[text() || json() || arrayBuffer()]` <-- also promise
if (res.ok) {
// status code was 200-299
} else {
// status was something else
}
}, function(error) {
console.error('failed due to network error or cross domain')
})
I am working Angular SPA application.
I am using SP.js & SP.Requestor.js which is useful to upload the same but none seems to be working of Angular 4 App.
UploadFile(event: any) {
let fileList: FileList = event.target.files;
let fileName;
if (fileList.length != 0) {
this.getFileBuffer(fileList[0]).then(result => {
this.uploadFile(result, fileList[0].name, "POC").then(result => {
alert("added");
});
});
// this.fileUpload(fileList[0], "RetrievePOC", fileList[0].name);
}
}
getFileBuffer(file) {
return new Promise((resolve, reject) => {
var myReader: FileReader = new FileReader();
myReader.onload = function (e) {
resolve(myReader.result);
//resolve(e.target);
}
myReader.onerror = function (e) {
//deferred.reject(e.target.error);
}
//myReader.readAsArrayBuffer(file);
myReader.readAsBinaryString(file);
//resolve(file);
//return deferred.promise();
});
};
uploadFile(file, fileName, libraryName) {
return new Promise((resolve, reject) => {
// Construct the endpoint - The GetList method is available for SharePoint Online only.
//var serverRelativeUrlToFolder = "decodedurl='" + "/" + libraryName + "'";
var endpoint = this.siteUrl + "/_api/web/lists/getByTitle('POC')/Files/add('" + fileName + "')";
const headers = {
"accept": "application/json;odata=verbose",
"content-type": "application/json; odata=verbose",
};
//let fileData =this.convertDataBinaryString(file);
this.executeAsync(endpoint, file, headers).then(file => resolve(true)).catch(err => reject(err));
});
}
executeAsync(endPointUrl, data, requestHeaders) {
return new Promise((resolve, reject) => {
// using a utils function we would get the APP WEB url value and pass it into the constructor...
let executor = new SP.RequestExecutor(this.siteUrl);
// Send the request.
executor.executeAsync({
url: endPointUrl,
method: "POST",
body: data,
binaryStringRequestBody: true,
headers: requestHeaders,
success: offset => resolve(offset),
error: err => reject(alert(err.responseText))
});
});
}
Getting following error in executeAsync methond :
ErrorPage.PostMessage: Origin=https://localhost:44316,
Data={"command":"Query","postMessageId":"SP.RequestExecutor3","responseAvailable":false,"errorCode":-1007,"errorMessage":"Correlation
ID: e12d5a9e-b0d6-0000-745f-24b31dd971a6"}
vendor.js?v=T82_qgC1tKr4vAoag-4pr9ch_dUDSit3nEgaqP4H0Ec:12090 ERROR
Error: Uncaught (in promise): undefined
at resolvePromise (vendor.js?v=T82_qgC1tKr4vAoag-4pr9ch_dUDSit3nEgaqP4H0Ec:87020
)
We can use jQuery to upload file, here is a demo for your reference:
HTML:
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.min.js" type="text/javascript"></script>
<input id="getFile" type="file"/><br />
<input id="displayName" type="text" value="Enter a unique name" /><br />
<input id="addFileButton" type="button" value="Upload" onclick="uploadFile()"/>
JS code:
'use strict';
jQuery(document).ready(function () {
// Check for FileReader API (HTML5) support.
if (!window.FileReader) {
alert('This browser does not support the FileReader API.');
}
});
// Upload the file.
// You can upload files up to 2 GB with the REST API.
function uploadFile() {
// Define the folder path for this example.
var serverRelativeUrlToFolder = '/shared documents';
// Get test values from the file input and text input page controls.
var fileInput = jQuery('#getFile');
var newName = jQuery('#displayName').val();
// Get the server URL.
var serverUrl = _spPageContextInfo.webAbsoluteUrl;
// Initiate method calls using jQuery promises.
// Get the local file as an array buffer.
var getFile = getFileBuffer();
getFile.done(function (arrayBuffer) {
// Add the file to the SharePoint folder.
var addFile = addFileToFolder(arrayBuffer);
addFile.done(function (file, status, xhr) {
// Get the list item that corresponds to the uploaded file.
var getItem = getListItem(file.d.ListItemAllFields.__deferred.uri);
getItem.done(function (listItem, status, xhr) {
// Change the display name and title of the list item.
var changeItem = updateListItem(listItem.d.__metadata);
changeItem.done(function (data, status, xhr) {
alert('file uploaded and updated');
});
changeItem.fail(onError);
});
getItem.fail(onError);
});
addFile.fail(onError);
});
getFile.fail(onError);
// Get the local file as an array buffer.
function getFileBuffer() {
var deferred = jQuery.Deferred();
var reader = new FileReader();
reader.onloadend = function (e) {
deferred.resolve(e.target.result);
}
reader.onerror = function (e) {
deferred.reject(e.target.error);
}
reader.readAsArrayBuffer(fileInput[0].files[0]);
return deferred.promise();
}
// Add the file to the file collection in the Shared Documents folder.
function addFileToFolder(arrayBuffer) {
// Get the file name from the file input control on the page.
var parts = fileInput[0].value.split('\\');
var fileName = parts[parts.length - 1];
// Construct the endpoint.
var fileCollectionEndpoint = String.format(
"{0}/_api/web/getfolderbyserverrelativeurl('{1}')/files" +
"/add(overwrite=true, url='{2}')",
serverUrl, serverRelativeUrlToFolder, fileName);
// Send the request and return the response.
// This call returns the SharePoint file.
return jQuery.ajax({
url: fileCollectionEndpoint,
type: "POST",
data: arrayBuffer,
processData: false,
headers: {
"accept": "application/json;odata=verbose",
"X-RequestDigest": jQuery("#__REQUESTDIGEST").val(),
"content-length": arrayBuffer.byteLength
}
});
}
// Get the list item that corresponds to the file by calling the file's ListItemAllFields property.
function getListItem(fileListItemUri) {
// Send the request and return the response.
return jQuery.ajax({
url: fileListItemUri,
type: "GET",
headers: { "accept": "application/json;odata=verbose" }
});
}
// Change the display name and title of the list item.
function updateListItem(itemMetadata) {
// Define the list item changes. Use the FileLeafRef property to change the display name.
// For simplicity, also use the name as the title.
// The example gets the list item type from the item's metadata, but you can also get it from the
// ListItemEntityTypeFullName property of the list.
var body = String.format("{{'__metadata':{{'type':'{0}'}},'FileLeafRef':'{1}','Title':'{2}'}}",
itemMetadata.type, newName, newName);
// Send the request and return the promise.
// This call does not return response content from the server.
return jQuery.ajax({
url: itemMetadata.uri,
type: "POST",
data: body,
headers: {
"X-RequestDigest": jQuery("#__REQUESTDIGEST").val(),
"content-type": "application/json;odata=verbose",
"content-length": body.length,
"IF-MATCH": itemMetadata.etag,
"X-HTTP-Method": "MERGE"
}
});
}
}
// Display error messages.
function onError(error) {
alert(error.responseText);
}
I am downloading some file with $http which is calling java rest API, it responds in buffered array. I have to persist the download even if the browser reloaded. I am not sure whether it is possible or not within the client side itself.
Here is my code :
$http({
url : downloadUrl,
method : 'POST',
data : {filePath : path},
cache: true,
headers : {
'Content-type' : 'application/json',
'Accept': 'application/zip'
},
eventHandlers: {
progress: function (e) {
console.log('Progress :'+((e.loaded /
itemSelected.fileSize) * 100));
}
},
responseType : 'arraybuffer'
}).then(function (res) {
var blob = new Blob([res.data], {type:"application/octet-
stream"});
var filename = test.zip;
// using filesaver to download the file from blob
FileSaver.saveAs(blob, filename);
}, function (err) {
console.log(err);
});
Jeez... How about that title, terrible lol.
Well I am using jquery-comments by Viima. (http://viima.github.io/jquery-comments/)
I'm trying to use ajax commands to a Node.JS script that will update a JSON file. All of which is local, no crossing domains or anything.
Here is my Node.JS script:
var http = require('http');
var fs = require('fs');
http.createServer(function (req, res) {
fs.writeFile("/comments-data.json", "commentJSON", function(err) {
if(err) {
return console.log(err);
}
console.log("The file was saved!");
});
}).listen(8080, '127.0.0.1');
console.log('Server running at http://127.0.0.1:8080/');
Here is the Ajax post
postComment: function(commentJSON, success, error) {
$.ajax({
type: 'post',
url: 'http://127.0.0.1:8080',
data: commentJSON,
success: function(comment) {
success(comment)
},
error: error
});
},
I don't want to redirect. I just wanted to asynchronously show the new comment. Also this script will ultimately have to be able to handle video attachments as well and store the filepath inside the JSON file. But i believe, Jquery-comments just reads the file path from the JSON
Here is what the support site says for attachments
uploadAttachments: function(commentArray, success, error) {
var responses = 0;
var successfulUploads = [];
var serverResponded = function() {
responses++;
// Check if all requests have finished
if(responses == commentArray.length) {
// Case: all failed
if(successfulUploads.length == 0) {
error();
// Case: some succeeded
} else {
success(successfulUploads)
}
}
}
$(commentArray).each(function(index, commentJSON) {
// Create form data
var formData = new FormData();
$(Object.keys(commentJSON)).each(function(index, key) {
var value = commentJSON[key];
if(value) formData.append(key, value);
});
$.ajax({
url: '/api/comments/',
type: 'POST',
data: formData,
cache: false,
contentType: false,
processData: false,
success: function(commentJSON) {
successfulUploads.push(commentJSON);
serverResponded();
},
error: function(data) {
serverResponded();
},
});
});
}
});
I have a front end Canvas that I transform into a png file that I need to POST to a third party vendor's api. It passes back to node as a base64 file and I decode it, but when I attempt the upload, it gives me the following error:
Problem processing POST request: no Content-Type specified
However, I am clearly specifying the content type in my POST call. My end goal is to upload the file to my vendor's API.
Here are the key front end aspects:
var canvasImage = document.getElementById("c");
var img = canvas.toDataURL({
multiplier: canvasMultiplier
});
var fileTime = Date.now();
var myFileName = $scope.productCode + fileTime;
$scope.filenameForVendor = myFileName;
var filename = $scope.filenameForVendor;
$http.post('/postVendor', { filename: filename, file: img }).success(function (data) {
console.log("Uploaded to Vendor");
Here is the backend POST:
app.post('/postVendor', function (req, res, next) {
var filename = req.body.filename;
var file = req.body.file;
fileBuffer = decodeBase64Image(file);
request({
url: "http://myvendorapi/ws/endpoint",
method: "POST",
headers: {
'contentType': fileBuffer.type
},
body: fileBuffer.data
}, function (error, response, body) {
console.log(response);
});
})
// Decode file for upload
function decodeBase64Image(dataString) {
var matches = dataString.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/),
response = {};
if (matches.length !== 3) {
return new Error('Invalid input string');
}
response.type = matches[1];
response.data = new Buffer(matches[2], 'base64');
return response;
}
I can POST using AJAX on the front end, but because of CORS and the vendor blocking all but server side calls to the endpoints (and they don't have JSONP), I can't use this. They are allowing my IP through for testing purposes so only I can make this work from my machine:
var send = function (blob) {
var fileTime = Date.now();
var myFileName = $scope.productCode + fileTime;
$scope.filenameForVendor = myFileName;
var filename = $scope.filenameForVendor;
var formdata = new FormData();
formdata.append('File1', blob, filename);
$.ajax({
url: 'http://myvendorapi/ws/endpoint',
type: "POST",
data: formdata,
mimeType: "multipart/form-data",
processData: false,
contentType: false,
crossDomain: true,
success: function (result) {
console.log("Upload to Vendor complete!");
// rest of code here/including error close out
}
var bytes = atob(dataURL.split(',')[1])
var arr = new Uint8Array(bytes.length);
for (var i = 0; i < bytes.length; i++) {
arr[i] = bytes.charCodeAt(i);
}
send(new Blob([arr], { type: 'image/png' }));
Update:
I realized that contentType should be 'content-type'. When I did this, it creates an error of no boundary specified as I am trying multipart-form data (which I did all wrong). How can I pass formData to Node for uploading?
Update 2:
Per the advice offered, I tried using multer but am getting an ReferenceError: XMLHttpRequest is not defined.
Client side:
var fileTime = Date.now();
var myFileName = $scope.productCode + fileTime;
$scope.filenameForVendor = myFileName;
var filename = $scope.filenameForVendor;
var formdata = new FormData();
formdata.append('File1', blob, filename);
$http.post('/postVendor', formdata, { transformRequest: angular.identity, headers: { 'Content-Type': undefined } }).success(function (data) {
Server side:
app.post('/postVendor', function (req, res, next) {
var request = new XMLHttpRequest();
request.open("POST", "http://myvendorapi.net/ws/endpoint");
request.send(formData);
})
Why do you base64 encode the file?
You can upload raw file to your Node using FormData and you will not have to decode anything.
Front end
...
var request = new XMLHttpRequest();
request.open('POST', 'http://node.js/method');
request.send(formData); // vanilla
--- or ---
...
$http.post('http://node.js/method', formData, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
}); // angular
Back end
Just install request.
...
var request = require('request');
app.post('/method', function (req, res, next) {
// if you just want to push request you don't need to parse anything
req.pipe(request('http://vendor.net')).pipe(res);
}) // express