I'm building a library to "nodeize" the HTML5 File Api (currently in alpha)[0], to make it work with binary contents and don't have problems with charsets, I'm using Buffer[1] utility.
But the HTML5 File API uses Blob native object. Actualy I'm using the type 'application/octet-stream', and 'binary' from Buffer encoding. But, I want to use base64 in order to prevent any problem:
CoFS.prototype.writeFile = function (fileName, data, encoding, callback) {
var self = this;
if (Buffer.isBuffer(data)) {
callback = encoding;
encoding = undefined;
} else {
data = new Buffer(data, encoding);
}
this._ifready(function () {
self.getFileEntry(fileName, {create: true, exclusive: true}, function (err, fileEntry) {
if (err) return callback(new Error("Error getting file access " + err.message));
fileEntry.createWriter(function(fileWriter) {
fileWriter.onwriteend = function () {
callback(null);
};
fileWriter.onerror = function(e) {
callback(new Error(e.toString()));
};
var blob = new Blob([data.toString('binary')], {type: 'application/octet-stream'});
fileWriter.write(blob);
}, function () {
callback(new Error('Error writing ' + fileName));
});
});
});
};
Exacts on:
var blob = new Blob([data.toString('binary')], {type: 'application/octet-stream'});
I red the MDN page[2] but I didn't see anything about the encoding.
Is there any way to accomplish something like that?:
var blob = new Blob([data.toString('base64')], {type: 'application/octet-stream', encoding: 'base64'});
Thankyou.
0: https://github.com/exos/cofs
1: https://github.com/anodynos/node2web_buffer
2: https://developer.mozilla.org/en-US/docs/Web/API/Blob
I realized the Buffer object is extended from Int8Array, and both are compatible.
My code now is:
var blob = new Blob(
[data], // Data is a buffer!
{
type: 'application/octet-stream'
}
);
fileWriter.write(blob);
And for read, I use readAsArrayBuffer method, you can do:
var d = new Int8Array(arr); // When arr is arrayBuffer
var buf = new Buffer(d); // And works!
This solve my problem. For convert base64 encoded content and use in a blob, with Buffer, can be:
var blob = new Blob(
[new Buffer(cleanData, 'base64')],
{
type: 'application/octet-stream'
}
);
Related
I am using React-Dropzone npm to use a nicely styled drag and drop out of the box file uploader. I got stuck on the fact that React-Dropzone as of version 8.2.0 didn't include the paths to the file, e.g. shortened it with just the image name. They do however, provide a Blob Url. I can't figure out how to convert a Blob-URL to a Base64 string and then send that to Cloudinary.
Another way can be:
const url = 'blob:http://uri';
const blobToBase64 = (url) => {
return new Promise((resolve, _) => {
// do a request to the blob uri
const response = await fetch(url);
// response has a method called .blob() to get the blob file
const blob = await response.blob();
// instantiate a file reader
const fileReader = new FileReader();
// read the file
fileReader.readAsDataURL(blob);
fileReader.onloadend = function(){
resolve(fileReader.result); // Here is the base64 string
}
});
};
// now you can get the
blobToBase64(url)
.then(base64String => {
console.log(base64String) // i.e: data:image/jpeg;base64,/9j/4AAQSkZJ..
});
// or with await/async
const file = await blobToBase64(url);
console.log(file) // i.e: data:image/jpeg;base64,/9j/4AAQSkZJ..
I've figured it out:
After a few hours, and some nice people posting on StackOverflow I have pieced it together.
const getBlobData = (file) => {
axios({
method: "get",
url: file, // blob url eg. blob:http://127.0.0.1:8000/e89c5d87-a634-4540-974c-30dc476825cc
responseType: "blob",
}).then(function (response) {
var reader = new FileReader();
reader.readAsDataURL(response.data);
reader.onloadend = function () {
var base64data = reader.result;
const formData = new FormData();
formData.append("file", base64data);
formData.append("api_key", YOUR_API_KEY);
// replace this with your upload preset name
formData.append("upload_preset", YOUR_PRESET_NAME);//via cloudinary
axios({
method: "POST",
url: "https://api.cloudinary.com/v1_1/YOUR_CLOUD_NAME/upload",
data: formData,
})
.then((res) => {
const imageURL = res.data.url;
//YOU CAN SET_STATE HOWEVER YOU WOULD LIKE HERE.
})
.catch((err) => {
console.log(err);
});
};
});
};
The frontend of the application having a file download option (which can be in the following format: xlsx, csv, dat).
For that, I use fileSaver.js
Everything works fine for the format .dat/.csv but for the .xlsx it does not work the files are corrupted.
I tested the conversion with the following formats :
utf8
base64
binary
Here's how I do :
// /* BACK */ //
// data is
fs.readFile(filePath, (err, data) {...})
// the api give this answer the important part is "filename" & "data"
{"status":"ok","context":"writing the intermediate file","target":"/temp/","fileName":"name.xlsx","data":{"type":"Buffer","data":[72,82,65,67,67,69,83,83,32,10]}}
// /* FRONT */ //
let json = JSON.stringify(data)
let buffer = Buffer.from(JSON.parse(json).data)
let read = buffer.toString('utf8')
let blob = new Blob([read])
FileSaver.saveAs(blob, fileName)
Ok for anybody who pass in this topic, my solution :
(keep in mind the real better solution for dl a file : send file in api response with header 'Content-disposition' or use express for that like this)
The back (Node) work like this :
fs.readFile(filePath, (err, data) => {
if (err) {
console.log(`-------- oups error - read --------`)
console.log(err)
res.send({ status: `erreur`, context: `read the source file`, id: err.errno, code: err.code, message: err.message.replace(/\\/g, '/') })
} else {
res.send({ status: `ok`, context: `send data file`, target: target, fileName: fileName, data: data })
}
})
Here :
target is the path for the front with the name of the file and his
extension (/path/name.ext)
fileName is juste the name and the extension (name.ext)
data is the data send by the readFile ({"type":"Buffer","data":[72,82,65,67,67,69,83,83,32,10]})
The front (React) work like this :
fetch(targetUrl)
.then(res => res.json())
.then(res => {
if (res.status !== `ok`) {
this.setState({
errorDlFile: true,
errorDlFile_context: res.context,
errorDlFile_id: res.id,
errorDlFile_code: res.code,
errorDlFile_message: res.message
})
} else {
const target = res.target
const fileName = res.fileName
const data = res.data
const splitName = res.fileName.split('.')
const format = splitName[splitName.length-1]
// file saver blob solution
let json = JSON.stringify(data)
let buffer = Buffer.from(JSON.parse(json).data)
let readUTF8 = buffer.toString('utf8')
let blob = ''
if (format === 'xlsx') {
blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
} else if (format === 'csv') {
blob = new Blob([readUTF8], { type: 'application/vnd.ms-excel' })
} else {
blob = new Blob([readUTF8])
}
FileSaver.saveAs(blob, fileName)
}
})
#Hadock
if you want to download file with .csv extension then you need to pass the type of file like
let blob = new Blob([csv], { type: 'application/vnd.ms-excel' });
instead of
let blob = new Blob([read])
and don't forgot to send filename with extension (test.csv).
For excel file I used different plugin exceljs demo.
you can't save json data directly to .xlsx file, you can convert json data to excel format using library like 'sheetjs' (https://sheetjs.com/)
var ws_name = filename;//"SheetJS";
var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
/* add worksheet to workbook */
wb.SheetNames.push(ws_name);
wb.Sheets[ws_name] = ws;
var wbout = XLSX.write(wb, { bookType: 'xlsx', bookSST: true, type: 'binary' });
saveAs(new Blob([s2ab(wbout)], { type: "application/octet-stream" }), filename + ".xlsx")
When I try to upload, I get the following error:
Error code:1363030,
msg: Your video upload timed out before it could be completed. This is probably because of a slow network connection or because the video you're trying to upload is too large. Please try again.
I'm using Facebook Javascript SDK 2.5.
What am I missing or wrong?
<script>
var files;
var fileData = '';
function handleFileSelect(evt) {
files = evt.target.files; // FileList object
var input = evt.target;
var reader = new FileReader();
reader.onload = function (e) {
fileData = e.target.result;
};
reader.readAsDataURL(input.files[0]);
// files is a FileList of File objects. List some properties.
var output = [];
for (var i = 0, f; f = files[i]; i++) {
output.push('<li class="list-group-item">', escape(f.name), '(', f.type || 'n/a', ') - ',
f.size, ' bytes','</li>');
}
document.getElementById('list').innerHTML = output.join('');
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
$(document).ready(function()
{
$("#upload").click(function(){
var token = $('#token').val();
FB.api(
"/me/videos",
"POST",
{
"access_token" : token,
"title" : 'test',
"source": fileData
},
function (response) {
if (response && !response.error) {
/* handle the result */
}
}
);
})
});
</script>
here is the sample site
Just hit this in Node.js. This Facebook error occurs if you don't specify the content type and file name of the attached file (i.e. if you pass it as an inline field value, not as an attached file).
Not sure how to do it via FB.api, but with request-promise module (and ES7 async functions via Babel) it looks like this:
import request from 'request-promise'
async function uploadVideoToFacebook (buf) {
let url = 'https://graph-video.facebook.com/v2.5/' + pageId + '/videos?access_token=' + pageToken
let formData = {
title: 'Video title',
description: 'Timeline message...',
source: {
value: buf,
options: {
filename: 'video.mp4',
contentType: 'video/mp4'
}
}
}
return await request({ method: 'POST', url, formData })
}
and with XMLHttpRequest on the client-side you would do something like:
var blob = new Blob(videoDataHere, { type: 'video/mp4' })
var formData = new FormData();
formData.append('source', blob);
formData.append('message', 'Spartan Overlay');
var ajax = new XMLHttpRequest()
ajax.onreadystatechange = ...
ajax.open('POST', 'https://graph.facebook.com/' + userId + '/videos?access_token=' + accessToken, true)
ajax.send(formData)
If I post a PDF to my vendors API, they return me a .png file as a blob (see update 2 as I am now unsure if they are returning blob data).
I would like to push this into Azure Blob Storage. Using my code listed below, it pushes something in, but the file is corrupted. Example: downloading the .png from Azure Blob Storage and trying to open it with Paint gives the following error:
This is not a valid bitmap file, or its format is not currently
supported.
I have verified that the image is sent to me correctly as the vendor is able to open the .png on their side. I am wondering if I need to convert this to base64 or save it to a local Web directory before uploading it to Azure Blob Storage.
Here is my Angular front end Controller that calls my Node/Express backend for uploading to Azure once it receives the returned "image":
$.ajax({
url: 'http://myvendorsapi.net/uploadPDF,
type: "POST",
data: formdata,
mimeType: "multipart/form-data",
processData: false,
contentType: false,
crossDomain: true,
success: function (result) {
var containerName = 'container1';
var filename = 'Texture_0.png';
var file = result;
$http.post('/postAdvanced', { containerName: containerName, filename: filename, file: file }).success(function (data) {
console.log("success!");
}, function (err) {
//console.log(err);
});
},
error: function (error) {
console.log("Something went wrong!");
}
})
}
Here is my Node/Express backend that uploads the blob to Azure Blob Storage. It gives no error, but the file can't be opened/gives the error stated above when opened in Paint:
app.post('/postAdvanced', function (req, res, next) {
var containerName = req.body.containerName;
var filename = req.body.filename;
var file = req.body.file;
blobSvc.createBlockBlobFromText(containerName, filename, file, function (error, result, response) {
if (!error) {
res.send(result);
}
else {
console.log(error);
}
});
})
Update 1: The answer provided here allows me to pass in the URL of the vendors API for some endpoints: Download file via Webservice and Push it to Azure Blob Storage via Node/Express
It works as it writes the file at the endpoint to a temp folder. In my current scenario, I upload a PDF file and it returns an image file that I need to upload to Azure Blob Storage. Is there a way to use the answer here, but adjust it for a file that I already have (since it is returned to me) versus file streaming from a URL?
Update 2: In console logging the returned "file", it looks like it may be data. I am not sure, it looks like this:
Is this actually data, and if so, how do I make this into a file for upload?
UPDATE 3:
Since it appears that jQuery AJAX can't manage binary returns. I am able to "open" the blob using XMLHTTPResponse as follows, but I can't seem to push this into Azure as it gives me the following error:
TypeError: must start with number, buffer, array or string
Here is my request. Note that the file opens properly:
var form = document.forms.namedItem("fileinfo");
form.addEventListener('submit', function (ev) {
var oData = new FormData(form);
var xhr = new XMLHttpRequest();
xhr.responseType = "arraybuffer";
xhr.open("POST", "http://myvendorsapi/Upload", true);
xhr.onload = function (oEvent) {
if (xhr.status == 200) {
var blob = new Blob([xhr.response], { type: "image/png" });
var objectUrl = URL.createObjectURL(blob);
window.open(objectUrl);
console.log(blob);
var containerName = boxContainerName;
var filename = 'Texture_0.png';
$http.post('/postAdvanced', { containerName: containerName, filename: filename, file: blob }).success(function (data) {
//console.log(data);
console.log("success!");
}, function (err) {
//console.log(err);
});
} else {
oOutput.innerHTML = "Error " + xhr.status + " occurred when trying to upload your file.<br \/>";
}
};
xhr.send(oData);
ev.preventDefault();
}, false);
createBlockBlobFromText will work with either string or buffer. You might need a buffer to hold the binary content due to a known issue of jQuery.
For a workaround, there are several options:
Option 1: Reading binary filesusing jquery ajax
Option 2: Use native XMLHttpRequest
Option 3: Write frontend with Node as well and browserify it.
Your frontend code may look like:
var request = require('request');
request.post('http://myvendorsapi.net/uploadPDF', function (error, response, body) {
if (!error && response.statusCode == 200) {
var formData = {
containerName: 'container1',
filename: 'Texture_0.png',
file: body
};
request.post({ uri: '/postAdvanced', formData: formData }, function optionalCallback(err, httpResponse, body) {
if (err) {
return console.error('upload failed:', err);
}
console.log('Upload successful! Server responded with:', body);
});
} else {
console.log('Get snapshot failed!');
}
});
Then the backend code may look like:
app.post('/postAdvanced', function (req, res, next) {
var containerName = req.body.containerName;
var filename = req.body.filename;
var file = req.body.file;
if (!Buffer.isBuffer(file)) {
// Convert 'file' to a binary buffer
}
var options = { contentType: 'image/png' };
blobSvc.createBlockBlobFromText(containerName, filename, file, options, function (error, result, response) {
if (!error) {
res.send(result);
} else {
console.log(error);
}
});
})
Below I have the code to upload the image as binary in angular using FormData.
The server code will be the code to handle a regular file upload via a form.
var form = document.forms.namedItem("fileinfo");
form.addEventListener('submit', function (ev) {
var oData = new FormData(form);
var xhr = new XMLHttpRequest();
xhr.responseType = "arraybuffer";
xhr.open("POST", "http://vendorapi.net/Upload", true);
xhr.onload = function (oEvent) {
if (xhr.status == 200) {
var blob = new Blob([xhr.response], { type: "image/png" });
//var objectUrl = URL.createObjectURL(blob);
//window.open(objectUrl);
//console.log(blob);
var formData = new FormData()
formData.append('file', blob);
formData.append('containerName', boxContainerName);
formData.append('filename', 'Texture_0.png');
$http.post('/postAdvancedTest', formData, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
}).success(function (data) {
//console.log(data);
console.log("success!");
// Clear previous 3D render
$('#webGL-container').empty();
// Generated new 3D render
$scope.generate3D();
}, function (err) {
//console.log(err);
});
} else {
oOutput.innerHTML = "Error " + xhr.status + " occurred when trying to upload your file.<br \/>";
}
};
xhr.send(oData);
ev.preventDefault();
}, false);
I have solved the issue (thanks to Yang's input as well). I needed to base64 encode the data on the client side before passing it to node to decode to a file. I needed to use XMLHTTPRequest to get binary data properly, as jQuery AJAX appears to have an issue with returning (see here: http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/).
Here is my front end:
var form = document.forms.namedItem("fileinfo");
form.addEventListener('submit', function (ev) {
var oData = new FormData(form);
var xhr = new XMLHttpRequest();
xhr.responseType = "arraybuffer";
xhr.open("POST", "http://vendorapi.net/Upload", true);
xhr.onload = function (oEvent) {
if (xhr.status == 200) {
var blob = new Blob([xhr.response], { type: "image/png" });
//var objectUrl = URL.createObjectURL(blob);
//window.open(objectUrl);
console.log(blob);
var blobToBase64 = function(blob, cb) {
var reader = new FileReader();
reader.onload = function() {
var dataUrl = reader.result;
var base64 = dataUrl.split(',')[1];
cb(base64);
};
reader.readAsDataURL(blob);
};
blobToBase64(blob, function(base64){ // encode
var update = {'blob': base64};
var containerName = boxContainerName;
var filename = 'Texture_0.png';
$http.post('/postAdvancedTest', { containerName: containerName, filename: filename, file: base64}).success(function (data) {
//console.log(data);
console.log("success!");
// Clear previous 3D render
$('#webGL-container').empty();
// Generated new 3D render
$scope.generate3D();
}, function (err) {
//console.log(err);
});
})
} else {
oOutput.innerHTML = "Error " + xhr.status + " occurred when trying to upload your file.<br \/>";
}
};
xhr.send(oData);
ev.preventDefault();
}, false);
Node Backend:
app.post('/postAdvancedTest', function (req, res) {
var containerName = req.body.containerName
var filename = req.body.filename;
var file = req.body.file;
var buf = new Buffer(file, 'base64'); // decode
var tmpBasePath = 'upload/'; //this folder is to save files download from vendor URL, and should be created in the root directory previously.
var tmpFolder = tmpBasePath + containerName + '/';
// Create unique temp directory to store files
mkdirp(tmpFolder, function (err) {
if (err) console.error(err)
else console.log('Directory Created')
});
// This is the location of download files, e.g. 'upload/Texture_0.png'
var tmpFileSavedLocation = tmpFolder + filename;
fs.writeFile(tmpFileSavedLocation, buf, function (err) {
if (err) {
console.log("err", err);
} else {
//return res.json({ 'status': 'success' });
blobSvc.createBlockBlobFromLocalFile(containerName, filename, tmpFileSavedLocation, function (error, result, response) {
if (!error) {
console.log("Uploaded" + result);
res.send(containerName);
}
else {
console.log(error);
}
});
}
})
})
I am unable to upload my images to AWS S3
{ [InvalidParameterType: Expected params.Body to be a string, Buffer,
Stream, Blob, or typed array object] message: 'Expected params.Body
to be a string, Buffer, Stream, Blob, or typed array object', code:
'InvalidParameterType',
Background
So this is what I am trying to do: first, I used Angular Image Cropper (http://andyshora.com/angular-image-cropper.html) to crop the image to a acceptable size for mobile. This gives a base64 URI.
Then, I used dataURItoBlob to convert the URI to blob, and attempt to upload to AWS S3.
The code looks like this:
Angular
$scope.dataURItoBlob= function(dataURI) {
var binary = atob(dataURI.split(',')[1]);
var array = [];
for(var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {type: 'image/png'});
}
$scope.uploadPic = function() {
var base64data = document.getElementById('base64').value;
console.log("the data is: "+ base64data);
$scope.pic.Body = $scope.dataURItoBlob(base64data);
console.log("the blob is: "+ $scope.pic.Body);
$http({
method: 'POST',
url: '/api/upload/picture',
data: $scope.pic
}).success(function (data) {
//success code
});
};
Node (backend)
exports.uploadPicture = function (req, res) {
if (!req.body.hasOwnProperty('Key') || !req.body.hasOwnProperty('Body')) {
res.statusCode = 400;
return res.send('Error 400: Post syntax incorrect.');
} else {
var key = req.body.Key;
var body = req.body.Body;
AWS.config.loadFromPath('./config.json');
var s3 = new AWS.S3();
s3.createBucket({Bucket: 'bucket'}, function() {
var params = {Bucket: 'bucket', Key: key, Body: body};
s3.putObject(params, function(err, data) {
if (err) {
console.log(err);
res.status(400).send('Bad Request.');
} else {
console.log("Successfully uploaded data to myBucket/myKey");
}
});
});
}
}
It fails because it is expecting a Blob but it is rejecting the Blob that I am sending in.. Please help!
Thanks!
what is inside your "var base64data"?
try this:
buf = new Buffer(req.body.imageBinary.replace(/^data:image\/\w+;base64,/, ""),'base64')
var data = {
Key: req.body.userId,
Body: buf,
ContentEncoding: 'base64',
ContentType: 'image/jpeg'
};
s3Bucket.putObject(data, function(err, data){
if (err) {
console.log(err);
console.log('Error uploading data: ', data);
} else {
console.log('succesfully uploaded the image!');
}
});
Hope that helps