JavaScript not able to rename file before upload - javascript

I am trying to upload file to aws s3. before i upload i want to rename it by adding timestamp to file name. but i am geting an error as 'Cannot assign to read only property 'name' of object '#''
here is the code
let file = e.target.files[0];
let timeStamp = (new Date()).getTime();
let fileExt = file.name.split('.')[file.name.split('.').length-1];
let fileNameWithoutExt = file.name.replace(`.${fileExt}`,'');
let newFileName = fileNameWithoutExt + '_' + timeStamp + '.' + fileExt;
file.name = newFileName;

Yep that sounds like a weird rule to set it as Read-only, but it's what it is...
So the workaround, not so hard, is to create a new File object from your previous one...
var previous_file = new File(['foo'], 'file.txt', {type: 'text/plain'});
try{
previous_file.name = 'hello.txt';
}
catch(e){}
console.log(previous_file.name); // didn't work
// so we just create a new File from it...
var new_file = new File([previous_file], 'hello.txt');
console.log(new_file);
But also note that if you need to support older browsers that don't support the File constructor, then you can override this file name in a FormData that you will send to your sever:
var file = new File(['foo'], 'text.txt', {type:'text/plain'});
var formdata = new FormData();
// this will override the file name
formdata.append('file', file, 'hello.txt');
// and now you can send this formdata through xhr
// for demo, we will just log its content
for(let entry of formdata.entries()) {
console.log(entry);
}

The append() method of FormData accepts a third optional filename parameter.
// new file name as a variable with timestamp
const newName = new Date().getTime() + event.target.files[0].name;
fd.append('file[]', event.target.files[0], newName);

You can't change a name of an already created file.
You can create
new instance of file with new file name, like in a post above.But
File constroctor is not supported by all browsers (is not supported at IE and EDGE supporting table).
You can put new
file name to key property of your amazon upload
https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html
instead of key = "folder1/folder2/${filename}"
you can write key = "folder1/folder2/youfilename.txt"

Related

Javascript formdata: encrypt files before appending

I need to modify existing frontend (angular) code that involves uploading files to a server. Now the files need to be encrypted before being uploaded.
The current approach uses FormData to append a number of files and send them in a single request as shown below:
function uploadFiles(wrappers){
var data = new FormData();
// Add each file
for(var i = 0; i < wrappers.length; i++){
var wrapper = wrappers[i];
var file = wrapper.file;
data.append('file_' + i, file);
}
$http.post(uri, data, requestCfg).then(
/*...*
I have been using Forge in other projects, but never in this sort of context and don't really see how to encrypt files on the fly and still append them as FormData contents.
Forge provides an easy API:
var key = forge.random.getBytesSync(16);
var iv = forge.random.getBytesSync(8);
// encrypt some bytes
var cipher = forge.rc2.createEncryptionCipher(key);
cipher.start(iv);
cipher.update(forge.util.createBuffer(someBytes));
cipher.finish();
var encrypted = cipher.output;
The backend recieves files using Formidable and all the file hanlding is already wired. I would thus like to stick to using the existing front-end logic but simply insert the encryption logic. In that, it's not the entire formdata that must be encrypted... I haven't found a good lead yet to approach this.
Suggestions are very welcome!
Ok, found a solution and added the decrypt code as well. This adds a layer of async code.
function appendFile(aFile, idx){
// Encrypt if a key was provided for this protocol test
if(!key){
data.append('dicomfile_' + idx, file);
appendedCount++;
onFileAppended();
}
else{
var reader = new FileReader();
reader.onload = function(){
// 1. Read bytes
var arrayBuffer = reader.result;
var bytes = new Uint8Array(arrayBuffer); // byte array aka uint8
// 2. Encrypt
var cipher = forge.cipher.createCipher('AES-CBC', key);
cipher.start({iv: iv});
cipher.update(forge.util.createBuffer(bytes));
cipher.finish();
// 3. To blob (file extends blob)
var encryptedByteCharacters = cipher.output.getBytes(); // encryptedByteCharacters is similar to an ATOB(b64) output
// var asB64 = forge.util.encode64(encryptedBytes);
// var encryptedByteCharacters = atob(asB64);
// Convert to Blob object
var blob = byteCharsToBlob(encryptedByteCharacters, "application/octet-stream", 512);
// 4. Append blob
data.append('dicomfile_' + idx, blob, file.name);
// Decrypt for the sake of testing
if(true){
var fileReader = new FileReader();
fileReader.onload = function() {
arrayBuffer = this.result;
var bytez = new Uint8Array(arrayBuffer);
var decipher = forge.cipher.createDecipher('AES-CBC', key);
decipher.start({iv: iv});
decipher.update(forge.util.createBuffer(bytez));
decipher.finish();
var decryptedByteCharacters = decipher.output.getBytes();
var truz = bytes === decryptedByteCharacters;
var blob = byteCharsToBlob(decryptedByteCharacters, "application/octet-stream", 512);
data.append('decrypted_' + idx, blob, file.name + '.decrypted');
appendedCount++;
onFileAppended();
};
fileReader.readAsArrayBuffer(blob);
}
else{
// z. Resume processing
appendedCount++;
onFileAppended();
}
}
// Read file
reader.readAsArrayBuffer(aFile);
}
}
function onFileAppended(){
// Only proceed when all files were appended and optionally encrypted (async)
if(appendedCount !== wrappers.length) return;
/* resume processing, upload or do whathever */

gzip form input files

I am extremely new to javascript/web programming. The main use of my upload form are csv files mostly. I am already using pako to gzip my json (in the request url).
How can I gzip the files before they are sent to the server?
This is roughly how I construct the formdata
$.each($("input[type=file]"), function(i, obj) {
$.each(obj.files, function(j, file) {
formData.append(obj.name, file); // we need to gzip the data
})
});
Edit1: I've managed to (I think) gzip the files using pako, but there's 1 issue - async problems. This is my new code:
$.each($("input[type=file]"), function(i, obj) {
$.each(obj.files, function(j, file) {
formData.append(obj.name, file); // we need to gzip the data
var r = new FileReader();
r.onload = function(){
var zippedResult = pako.gzip(r.result);
var oMyBlob = new Blob(zippedResult, {type : file.type}); // the blob
formData.append(obj.name, oMyBlob); // we need to gzip the data
};
r.readAsArrayBuffer(file);
})
});
// Time to send the formData!
$.ajax({......
As you can see the issue happens since the onload function is only ran after ajax has executed, so the formData is blank
edit2: I'm attempting to create a onchange event for the input files, so this is what I have come up with so far. There is a problem though - it doesn't seems to be zipping correctly. Data type issues?
$("input[type=file]").change(function (event){
var fileList = this.files;
$.each(fileList,function(i,file){
var r = new FileReader();
r.onload = function(){
var zippedResult = pako.gzip(r.result);
var oMyBlob = new Blob(zippedResult, {type : file.type});
app.formData.append(event.target.name, oMyBlob, file.name);
};
r.readAsArrayBuffer(file);
});
});
This is what I've done - note that the formData is a global variable. Take note to clear the formData when you've submitted, if not it will just keep increasing. Also if you re-select a file, it will be appended onto the form (which might not be what you want) - I have not yet found a way around it.
$("input[type=file]").change(function (event){
var fileList = this.files;
$.each(fileList,function(i,file){
var r = new FileReader();
r.onload = function(){
var convertedData = new Uint8Array(r.result);
// Zipping Uint8Array to Uint8Array
var zippedResult = pako.gzip(convertedData, {to : "Uint8Array"});
// Need to convert back Uint8Array to ArrayBuffer for blob
var convertedZipped = zippedResult.buffer;
var arrayBlob = new Array(1);
arrayBlob[0] = convertedZipped;
// Creating a blob file with array of ArrayBuffer
var oMyBlob = new Blob(arrayBlob , {type : file.type} ); // the blob (we need to set file.type if not it defaults to application/octet-stream since it's a gzip, up to you)
app.formData.append(event.target.name, oMyBlob, file.name); // we need to gzip the data
};
r.readAsArrayBuffer(file);
});
});
I have added gzip to JSZip.
I need JSZip and gzip for my web page, and JSZip has all the ingredients, but hides them in ways I can't crack
JSZip is is much better designed with the ability to process big files in chunked / streaming mode. I don't think pako alone does that. I am using both ZIP files and gzip for my project, so I figured basing them on the same package would be useful.

Apps Script Advanced Drive API Service - newFile() Method not Creating a File

The newFile() method of the Advanced Drive API Service seems to return an object, but not create a file in my Google Drive. I can't find any documentation for this. Here is code that I tried, and the results I got.
function makeNewDoc() {
var file = Drive.newFile();
Logger.log('file ' + file);
file = {one: "value One"};
Logger.log('file ' + file);
Logger.log("file['one'] " + file['one']);
};
The LOGS print this:
file {}
file [object Object]
file['one'] value One
I tried adding a string inside the newFile() parenthesis:
function makeNewDoc() {
var file = Drive.newFile("some text");
Logger.log('file ' + file);
file = {one: "value One"};
Logger.log('file ' + file);
Logger.log("file['one'] " + file['one']);
for (var key in file) {
Logger.log('key: ' + key);
Logger.log('value: ' + file[key]);
};
};
That didn't produce an error, but it doesn't do anything either. Or I don't know what it's doing.
I've checked my Google drive many times after running the code many times, and there is never a new file in my Drive.
The newFile() method returns an object, that I can put new properties into, and then enumerate. But other than that, I have no idea what it does, or what use it has.
So, if anyone can tell me what the newFile() method, of the Drive API does, I would really like to know.
Below is an example how to make a new document with advanced Drive service
function createNewDoc(title,content,folderId){
var content = content || "";
// neither kind or mimeType properties seem to be necessary
// for Doc to be created, but are being included anyhow
var resource = {
title: title,
parents: [
{
"id": folderId,
"kind": "drive#fileLink"
}
],
mimeType: 'application/vnd.google-apps.document'
};
var blob = Utilities.newBlob(content);
var newfile = Drive.Files.insert(resource, blob, {"convert":"true"});
return newFile;
}
I used the answer code and there was a problem when creating the blob as HTML. The below code converts HTML as a string to a blob to be inserted into a document.
var content = '<!DOCTYPE html><html><body><b>Hello</b></body><html>'
var blob = Utilities.newBlob("").setDataFromString(content,'UTF-8').setContentType("text/html")
var newfile = Drive.Files.insert(resource, blob, {"convert":"true"});

How to create File object from Blob?

DataTransferItemList.add allows you to override copy operation in javascript. It, however, only accepts File object.
Copy event
The code in my copy event:
var items = (event.clipboardData || event.originalEvent.clipboardData);
var files = items.items || items.files;
if(files) {
var blob = Blob.fromDataURL(_this.editor.selection.getSelectedImage().toDataURL("image/png"));
files.add(blob);
}
The error in chrome:
Uncaught TypeError: Failed to execute add on DataTransferItemList: parameter 1 is not of type File.
Trying the new File(Blob blob, DOMString name)
In Google Chrome I tried this, according to the current specification:
var blob = Blob.fromDataURL(_this.editor.selection.getSelectedImage().toDataURL("image/png"));
var file = new File(blob, "image.png");
Problem here is, that Google Chrome doesn't stick to specifications very much.
Uncaught TypeError: Failed to construct File: Illegal constructor
Neither does Firefox in this case:
The method parameter is missing or invalid.
Trying the new File([Mixed blobParts], DOMString name, BlobPropertyBag options)
Solution suggested by #apsillers doesn't work too. This is non stadard method used (but useless) in both Firefox and Chrome.
Binary data
I tried to avoid blob, but the file constructor failed anyway:
//Canvas to binary
var data = atob( //atob (array to binary) converts base64 string to binary string
_this.editor.selection.getSelectedImage() //Canvas
.toDataURL("image/png") //Base64 URI
.split(',')[1] //Base64 code
);
var file = new File([data], "image.png", {type:"image/png"}); //ERROR
You can try that in console:
Chrome <38:
Chrome >=38:
Firefox:
Blob
Passing Blob is probably correct and works in Firefox:
var file = new File([new Blob()], "image.png", {type:"image/png"});
Firefox:
Chrome <38:
Chrome >=38:
Q: So how can I make File from Blob?
Note: I added more screenshots after #apsillers reminded me to update Google Chrome.
The File constructor (as well as the Blob constructor) takes an array of parts. A part doesn't have to be a DOMString. It can also be a Blob, File, or a typed array. You can easily build a File out of a Blob like this:
new File([blob], "filename")
This was the complete syntax which I had to use to convert a blob into a file, which I later had to save to a folder using my server.
var file = new File([blob], "my_image.png",{type:"image/png", lastModified:new Date().getTime()})
this works with me, from canvas to File [or Blob], with filename!
var dataUrl = canvas.toDataURL('image/jpeg');
var bytes = dataUrl.split(',')[0].indexOf('base64') >= 0 ?
atob(dataUrl.split(',')[1]) :
(<any>window).unescape(dataUrl.split(',')[1]);
var mime = dataUrl.split(',')[0].split(':')[1].split(';')[0];
var max = bytes.length;
var ia = new Uint8Array(max);
for (var i = 0; i < max; i++) {
ia[i] = bytes.charCodeAt(i);
}
var newImageFileFromCanvas = new File([ia], 'fileName.jpg', { type: mime });
Or if you want a blob
var blob = new Blob([ia], { type: mime });

How to do file upload in e2e AngularJS tests?

In one of my views, I have a file upload control. It supports file uploading either via drag and drop, or via the standard file dialog opened after a button click.
How to do this in my e2e tests1?
1 Just one of the two options will be enough
You can upload files using Javascript blobs. This requires the FileApi, which isn't compatible with older browsers (http://caniuse.com/fileapi). But since you mentioned using drag and drop uploads, which uses the FileApi, it shouldn't matter too much.
There are two ways you can upload files using the blob API. One is very easy and the other is simply a continuation of the first.
Using Javascript, you can create a new blob with:
var blob = new Blob("content", contentType);
For example, this will create a blob object that contains the text "Hello World!".
var foo = new Blob("Hello World!", {type: "text/plain"});
You could also use the following method is better for non-plaintext files, such as pdf's. You have to convert the file to Base64 (you can use something like this) and create the blob using the Base64 data.
Use this function (a slightly modified version of this) to create the blob.
function b64toBlob(b64Data, contentType, sliceSize) {
b64Data = b64Data.replace(/\s/g, '');
contentType = contentType || '';
sliceSize = sliceSize || 1024;
function charCodeFromCharacter(c) {
return c.charCodeAt(0);
}
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = Array.prototype.map.call(slice, charCodeFromCharacter);
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {type: contentType});
return blob;
}
For example, this will create a PDF blob object.
var pdf = "JVBERi0xLjQKJcfsj6IKNSAwIG9...=="; //base64 encoded file as a String
var pdfBlob = b64toBlob(pdf, "application/pdf", 1024);
After you create the blob with one of the methods above, it can be treated as a file. For example, you could put the file into a FormData object (if you're doing uploads like this):
var fd = new FormData();
fd.append("uploadedFile", pdfBlob, "My PDF.pdf"*);
*Filename parameter only seems to work on Chrome as of now.

Categories

Resources