Had the exact same issue today, so I'll provide an answer in TypeScript(3.7.5) based on what worked out for me.
Tips:
- FileAPI needs a Blob to work with (as others have also stated), it won't work with a file path.
The function:
static async createFile(path: string, name: string, type: string): Promise<File> {
let response = await fetch(path);
let data = await response.blob();
let metadata = {
type: type
};
return new File([data], name, metadata);
}
The call:
await createFile('../assets/images/someInterestingPNGImage.png', 'iAmAFile.png', 'image/png')
.then((file) => {
//do something with ur file.
console.log(file);
});
Answer from apfel on Stack OverflowHad the exact same issue today, so I'll provide an answer in TypeScript(3.7.5) based on what worked out for me.
Tips:
- FileAPI needs a Blob to work with (as others have also stated), it won't work with a file path.
The function:
static async createFile(path: string, name: string, type: string): Promise<File> {
let response = await fetch(path);
let data = await response.blob();
let metadata = {
type: type
};
return new File([data], name, metadata);
}
The call:
await createFile('../assets/images/someInterestingPNGImage.png', 'iAmAFile.png', 'image/png')
.then((file) => {
//do something with ur file.
console.log(file);
});
File API needs a Blob here is work-arround
var GetFileBlobUsingURL = function (url, convertBlob) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "blob";
xhr.addEventListener('load', function() {
convertBlob(xhr.response);
});
xhr.send();
};
var blobToFile = function (blob, name) {
blob.lastModifiedDate = new Date();
blob.name = name;
return blob;
};
var GetFileObjectFromURL = function(filePathOrUrl, convertBlob) {
GetFileBlobUsingURL(filePathOrUrl, function (blob) {
convertBlob(blobToFile(blob, 'testFile.jpg'));
});
};
var FileURL="test/test.jpg"
GetFileObjectFromURL(FileURL, function (fileObject) {
console.log(fileObject);
});
If we want to create a file in a folder (both of them do not exist), we can use this way.
Of course, in this example, the path of the file (folders) is first the number of the current year, then the number of the current month, and finally the current day
For the file name, the time of file creation is considered as the name
The value inside the file is also sent by the front-end request.body.values[0]
import fs from 'fs';
import path from 'path';
let d= new Date()
const directory = await './some-dir/'+d.getFullYear()+'/'+("0" + (d.getMonth() + 1)).slice(-2)+'/'+(d.getDay().toString().padStart(2, "0"))
let name= (d.getHours() + '-' + d.getMinutes() + '-' + d.getSeconds()).toString()+".txt"
fs.mkdirSync(directory, { recursive: true })
fs.writeFileSync(path.join(directory,name), request.body.values[0],"UTF8")
Run code snippetEdit code snippet Hide Results Copy to answer Expand
The following code should help:
fs.access("./directory-name", function (error) {
if (error) {
let directory = './some-dir/' + d.getFullYear() + '/' + ("0" + (d.getMonth() + 1)).slice(-2) + '/' + (d.getDay().toString().padStart(2, "0")) + '/' + d.getHours() + '/' + d.getMinutes() + '/' + d.getSeconds()
fs.mkdirSync(directory, { recursive: true })
} else {
// directory exists.
}
})
with javascript alone this move will create a security problem and I don't think it's possible to do. But on server side with some tool like Node.js you can by doing something like:
var fs = require("fs");
fs.mkdir("<your path>",callback);
manipulating client file with your js code often create security issues
No you can't do this using native Javascript.Native Javascript won't allow you to do any I/O in browser. But if its your necessary requirement then I would suggest you to use any server side tool like node.js. How to do in node.js?,You can get reference from @Moussa answer.
You have to understand that you can give either absolute path or relative path. Currently what you can do is
fs.writeFile('./niktoResults/result.txt', 'This is my text', function (err) {
if (err) throw err; console.log('Results Received');
});
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Here . refers to current directory. Therefore ./niktoResults refers to niktoResults folder in current directory.
Do this
const fs = require('fs');
const path = require('path');
let baseDir = path.join(__dirname, '/./niktoResults/');
fs.open(`${baseDir}+result.txt`, 'wx', (err, desc) => {
if(!err && desc) {
fs.writeFile(desc, 'sample data', (err) => {
// Rest of your code
if (err) throw err;
console.log('Results Received');
})
}
})
Node > 10.12.0
fs.mkdir now accepts a { recursive: true } option like so:
// Creates /tmp/a/apple, regardless of whether `/tmp` and /tmp/a exist.
fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => {
if (err) throw err;
});
Or with a promise:
fs.promises.mkdir('/tmp/a/apple', { recursive: true }).catch(console.error);
Notes:
In many cases, you should use
fs.mkdirSyncrather thanfs.mkdirIncluding a trailing slash is harmless / has no effect
mkdirSync/mkdirdo nothing, harmlessly, if the directory already exists (there’s no need to check for existence)
Node <= 10.11.0
You can solve this with a package like mkdirp or fs-extra. If you don't want to install a package, please see Tiago Peres França's answer below.
If you don't want to use any additional package, you can call the following function before creating your file:
var path = require('path'),
fs = require('fs');
function ensureDirectoryExistence(filePath) {
var dirname = path.dirname(filePath);
if (fs.existsSync(dirname)) {
return true;
}
ensureDirectoryExistence(dirname);
fs.mkdirSync(dirname);
}
Create a java.io.FileWriter object first, then create a java.io.PrintWriter based on that FileWriter object. Then you can use PrintWriter.print() to add text to the file:
FileWriter fileWriter = new FileWriter("filename.ext", true);
PrintWriter out = new PrintWriter(fileWriter);
out.print("Text to go in the file");
out.println("Text to go in the file");
Note that you'll need to encase this code in a try-catch block to handle IOExceptions:
try {
FileWriter fileWriter = new FileWriter("filename.ext", true);
PrintWriter out = new PrintWriter(fileWriter);
out.print("Text to go in the file");
out.println("Text to go in the file");
} catch (java.io.IOException e) {
//handle
}
out.close(); //Don't forget to close the file when done
Let's assume that you successfully read the file into the String and the numbers into an array of numbers and the path is an absolute path.
String nameOfTheFile = "/absolute/path";
int[] numbersToStore = new int[]{1,2,3,4,5};
then you will create the .txt file on the location like this:
File txtWithNumbers = new File(nameOfTheFile + ".txt");
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(txtWithNumbers), charSet
)
);
try {
for(int i in numbersToStore) {
writer.write(i + "\n");
}
} finally {
writer.close();
}
A very minor improvement of the code by Awesomeness01 (no need for anchor tag) with addition as suggested by trueimage (support for IE):
// Function to download data to a file
function download(data, filename, type) {
var file = new Blob([data], {type: type});
if (window.navigator.msSaveOrOpenBlob) // IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
else { // Others
var a = document.createElement("a"),
url = URL.createObjectURL(file);
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
}
Tested to be working properly in Chrome, FireFox and IE10.
In Safari, the data gets opened in a new tab and one would have to manually save this file.
function download(text, name, type) {
var a = document.getElementById("a");
var file = new Blob([text], {type: type});
a.href = URL.createObjectURL(file);
a.download = name;
}
<a href="" id="a">click here to download your file</a>
<button onclick="download('file text', 'myfilename.txt', 'text/plain')">Create file</button>
Run code snippetEdit code snippet Hide Results Copy to answer Expand
And you would then download the file by putting the download attribute on the anchor tag.
The reason I like this better than creating a data url is that you don't have to make a big long url, you can just generate a temporary url.
This should work in recent versions of Node.js:
import fs from "fs";
import { Blob } from "buffer";
let buffer = fs.readFileSync("./your_file_name");
let blob = new Blob([buffer]);
Here is how you can upload a file from the drive to Firebase Storage:
let bucket = admin.storage().bucket();
let uploadRes = await bucket.upload(filePath, options);
You can find a description of the option in the google cloud storage docs.
You will most likely create a key in the google cloud console with permissions and export the file as a json. You will find this in the Google Cloud Platform console -> IAM & admin -> Service accounts -> Create service account (create it with a key). Once you exported the json, set the environment variable like this:
export GOOGLE_APPLICATION_CREDENTIALS='./the_exported_file.json'
PLEASE STORE THIS FILE SECURELY ON YOUR SERVER AS IT HAS READ AND WRITE ACCESS!
Below is a full example of an upload function that also saves the file under it's hash name.
<!-- language: typescript -->
import admin from "firebase-admin";
import path from 'path'
const sha256File = require('sha256-file');
const firebaseConfig = {
apiKey: "...",
authDomain: "abc.firebaseapp.com",
databaseURL: "https://abc.firebaseio.com",
projectId: "abc",
storageBucket: "abc.appspot.com",
messagingSenderId: "123",
appId: "1:123:web:xxx",
measurementId: "G-XXX"
};
admin.initializeApp(firebaseConfig);
async function uploadFile(filePath: string, uploadFolder: string, contentType: string, hash: string | undefined = undefined,)
: Promise<string> {
if (!hash) {
hash = await sha256File(filePath);
}
let bucket = admin.storage().bucket();
const ext = path.extname(filePath);
const uploadPath = uploadFolder + hash + ext;
const options = {destination: uploadPath};
console.debug("starting upload");
let uploadRes = await bucket.upload(filePath, options);
console.debug("finished upload");
let newMetadata = {
contentType: contentType
};
if(uploadRes) {
let file = uploadRes[0];
file.setMetadata(newMetadata).then(() => {
// Updated metadata for 'images/forest.jpg' is returned in the Promise
}).catch(function (error) {
console.error(error)
});
}
return uploadPath;
}
There is a library available to handle this, fswin:
https://www.npmjs.org/package/fswin
See the documentation for setAttribute here:
https://github.com/xxoo/node-fswin/wiki/setAttributes-and-setAttributesSync
In other words:
fswin.setAttributesSync('test.txt', { IS_HIDDEN: true });
Note that this requires a native compiler (it lists Visual Studio in the documentation, but perhaps others could be used).
You could try executing the DOS command attrib using child_process.spawn().
For those that are looking for a solution to this problem, I created an npm package to make it easier to retrieve files using Node's file system and convert them to JS File objects:
https://www.npmjs.com/package/get-file-object-from-local-path
This solves the lack of interoperability between Node's fs file system (which the browser doesn't have access to), and the browser's File object type, which Node cannot create.
3 steps are required:
- Get the file data in the Node instance and construct a LocalFileData object from it
- Send the created LocalFileData object to the client
- Convert the LocalFileData object to a File object in the browser.
// Within node.js
const fileData = new LocalFileData('path/to/file.txt')
// Within browser code
const file = constructFileFromLocalFileData(fileData)
I searched for File Systems examples and other possibilities and found nothing.
So I decide to create my own File object with JSON.
var imagePath = path.join('/images/logo.png', 'logo.png');
if (fs.statSync(imagePath)) {
var bitmap = fs.readFileSync(imagePath);
var bufferImage = new Buffer(bitmap);
Magic = mmm.Magic;
var magic = new Magic(mmm.MAGIC_MIME_TYPE);
magic.detectFile(imagePath, function(err, result) {
if (err) throw err;
datas = [{"buffer": bufferImage, "mimetype": result, "originalname": path.basename(imagePath)}];
var JsonDatas= JSON.parse(JSON.stringify(datas));
log.notice(JsonDatas);
});
}
The output :
{
buffer:
{
type: 'Buffer',
data:
[
255,
216,
255
... 24908 more items,
[length]: 25008
]
},
mimetype: 'image/png',
originalname: 'logo.png'
}
I think is probably not the best solution, but it give me what I want. If you have a better solution, you are welcome to suggest it.
You can pass-in Secrets as ENV variables.
Example:
...
steps:
- name: Git checkout
uses: actions/checkout@v2
- name: Use Node 12.x
uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Install Dependencies (prod)
run: yarn install --frozen-lockfile --production
- name: Run Tests (JEST)
run: yarn test --ci --silent --testPathIgnorePatterns=experimental
env:
CI: true
API_KEY: ${{ secrets.API_KEY }}
In Node.js you can access it via process.env.API_KEY.
I Find a way to achieve it although it might not be the best (And I'm definitly not bash expert)
So create a setEnv.sh file
mkdir env
echo "export const environment = { firebase_api_key : '$1' }" > env/env.ts
That take as your API key as first parameter, create a env folder and save TS code with your api key.
Then add this line
- run: sh setEnvironment.sh ${{ secrets.FIREBASE_API_KEY }}
Into your github action script, which will execute your script and set the Secret Key.
You'll now just have to use environment.firebase_api_key in your code.
Note: Your build needs to encrypt your key otherwise it will be exposed. But this can be usefull for example if you use API keys on your website and you also want your website code to be available in public on Github, without those plain keys.
For individual dirs:
var fs = require('fs');
var dir = './tmp';
if (!fs.existsSync(dir)){
fs.mkdirSync(dir);
}
Or, for nested dirs:
var fs = require('fs');
var dir = './tmp/but/then/nested';
if (!fs.existsSync(dir)){
fs.mkdirSync(dir, { recursive: true });
}
No, for multiple reasons.
The
pathmodule does not have anexists/existsSyncmethod. It is in thefsmodule. (Perhaps you just made a typo in your question?)The documentation explicitly discourage you from using
exists.fs.exists()is an anachronism and exists only for historical reasons. There should almost never be a reason to use it in your own code.In particular, checking if a file exists before opening it is an anti-pattern that leaves you vulnerable to race conditions: another process may remove the file between the calls to
fs.exists()andfs.open(). Just open the file and handle the error when it's not there.Since we're talking about a directory rather than a file, this advice implies you should just unconditionally call
mkdirand ignoreEEXIST.In general, you should avoid the *
Syncmethods. They're blocking, which means absolutely nothing else in your program can happen while you go to the disk. This is a very expensive operation, and the time it takes breaks the core assumption of node's event loop.The *
Syncmethods are usually fine in single-purpose quick scripts (those that do one thing and then exit), but should almost never be used when you're writing a server: your server will be unable to respond to anyone for the entire duration of the I/O requests. If multiple client requests require I/O operations, your server will very quickly grind to a halt.
The only time I'd consider using *
Syncmethods in a server application is in an operation that happens once (and only once), at startup. For example,requireactually usesreadFileSyncto load modules.Even then, you still have to be careful because lots of synchronous I/O can unnecessarily slow down your server's startup time.
Instead, you should use the asynchronous I/O methods.
So if we put together those pieces of advice, we get something like this:
function ensureExists(path, mask, cb) {
if (typeof mask == 'function') { // Allow the `mask` parameter to be optional
cb = mask;
mask = 0o744;
}
fs.mkdir(path, mask, function(err) {
if (err) {
if (err.code == 'EEXIST') cb(null); // Ignore the error if the folder already exists
else cb(err); // Something else went wrong
} else cb(null); // Successfully created folder
});
}
And we can use it like this:
ensureExists(__dirname + '/upload', 0o744, function(err) {
if (err) // Handle folder creation error
else // We're all good
});
Of course, this doesn't account for edge cases like
- What happens if the folder gets deleted while your program is running? (assuming you only check that it exists once during startup)
- What happens if the folder already exists, but with the wrong permissions?
You could use the mkdirp module, it handles creating recursive directories if they don't exist etc.
Sample code:
var mkdirp = require('mkdirp');
mkdirp('/tmp/foo/bar/baz', function (err) {
if (err) console.error(err)
else console.log('pow!')
});
You can do this using the File System module using the mkdir command