I currently have a function that prompts the user to download a JSON file:
function downloadObjectAsJson (exportObj, exportName) {
const dataStr = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(exportObj))
let downloadAnchorNode = document.createElement('a')
downloadAnchorNode.setAttribute('href', dataStr)
downloadAnchorNode.setAttribute('download', exportName + '.json')
document.body.appendChild(downloadAnchorNode) // required for firefox
downloadAnchorNode.click()
downloadAnchorNode.remove()
}
Is there a way to get the path the user selected to download this file to? Just need it to be displayed on the UI.
There are some API available that somehow allows access to the client file system like this, which lists down all the files in the selected directory:
async function listFilesInDirectory () {
const dirHandle = await window.showDirectoryPicker()
const promises = []
for await (const entry of dirHandle.values()) {
if (entry.kind !== 'file') {
break
}
promises.push(entry.getFile().then((file) => `${file.name} (${file.size})`))
}
console.log(await Promise.all(promises))
}
So I thought there might be some way to also get the path selected by the user when saving files.
Any other suggestions/means are welcome.
Related
I am trying to create a desktop launcher application in Electron that reads the number of files in the user's desktop and gathers the information from each file found. I am gathering the files and constructing the path based on the directory, but all I can get is the filenames. I am not sure how to store the file itself and extract the desktop icon from it. I haven't seen many solutions to it, other than using the AxtiveXobject however supposedly certain implementations do not work in the latest nodejs. Here is my code so far.
//requiring path and fs modules
const path = require('path');
const fs = require('fs');
//gets home directory
const homedir = require('os').homedir();
//specifies to desktop
const dir = `${homedir}/Desktop`;
var walk = require('walk');
var filepaths = [];
//storing desktop path
var desktopDir = dir;
console.log(desktopDir);
//gets the desktop files and paths
function getDesktopFiles(_dir){
//read directory
fs.readdir(_dir, (err, files) => {
if (err)
console.log(err);
else {
files.forEach(_file => {
//console.log(_file);
let _p = _dir + '/'+_file;
//changes slashing for file paths
let _path = _p.replace(/\\/g, "/");
filepaths.push(_path);
})
}
})
for(let p of filepaths){
console.log(p);
}
}
getDesktopFiles(desktopDir);
Here is a quick snippet of code which works for me in an Electron renderer process; it has been successfully tested on both macOS and Linux, and should be platform-independent.
It lists all the files located on the user's desktop and displays each file's icon and name at the end of the HTML page; it makes use of the following Electron API functions:
app.getPath
app.getFileIcon
image.toDataURL
image.getSize
const { app, nativeImage } = require ('electron').remote;
const path = require ('path');
const fs = require ('fs');
//
const desktopPath = app.getPath ('desktop');
let filePaths = fs.readdirSync (desktopPath);
for (let filePath of filePaths)
{
app.getFileIcon (filePath)
.then
(
(fileIcon) =>
{
let div = document.createElement ('div');
let img = document.createElement ('img');
img.setAttribute ('src', fileIcon.toDataURL ());
let size = fileIcon.getSize ();
img.setAttribute ('width', size.width);
img.setAttribute ('height', size.height);
div.appendChild (img);
div.appendChild (document.createTextNode (" " + path.basename (filePath)));
// For test purposes, add each file icon and name to the end of <body>
document.body.appendChild (div);
}
);
}
You may find some interesting hints about app.getFileIcon in the post: Is there a standard way for an Electron or Node.js app to access system-level icons?
Define a function for the same as:
function load(icon) {
if (cache[icon]) return cache[icon];
return cache[icon] = fs.readFileSync(__dirname + '/public/icons/' + icon, 'base64');
}
Here you can get the inspiration for the same.
I have a couple of functions I am running to download a zip file from Nexus, then unzip/extract the contents of the zip file, finally a search for a specific file type. All function work, however the synchronous search for some reason is not producing any results. If I simply run the download and extract functions in 1 script, then execute the search in another script I get my expected results. I am almost positive it is due to the search being synchronous whereas the download and extract are both async. Is there a quick way to add the find function at the end after the download & extract functions have run? Below is the code:
//npm modules
const fs = require('fs-extra');
const download = require('download');
const unzipper = require('unzipper');
const path = require('path');
//Custom Variables
const artifact = 'SOME_FILE.zip';
const repo = "SOME NEXUS REPOSITORY";
const url = "http://SOME URL/repository/";
const directory = 'SOME DIRECTORY';
//Get Artifact and Extract it to local directory function
const getArtifact = async () => {
const getArtifact = await download(url+repo, "./unzip")
const file = await fs.writeFileSync(directory+artifact, await download(url+repo))
const readStream = await fs.createReadStream(directory + artifact).pipe(unzipper.Extract({path:
directory}))
}
//Find function which should run after download and extract have been fulfilled
const findFile = function (dir, pattern) {
var results = [];
fs.readdirSync(dir).forEach(function (dirInner) {
dirInner = path.resolve(dir, dirInner);
var stat = fs.statSync(dirInner);
console.log(stat)
if (stat.isDirectory()) {
results = results.concat(findFile(dirInner, pattern));
}
if (stat.isFile() && dirInner.endsWith(pattern)) {
results.push(dirInner);
}
});
console.log(results)
return results;
};
//clear contents of directory before new download and extract
fs.emptyDirSync(directory)
//call download and extract function
getArtifact()
When I run "findFile" after the download & extract by itself in a separate script I get expected array output. However, when I try to incorporate (see below) this into the same script I get the an empty array:
getArtifact().then(function findFile (dir, pattern) {
var results = [];
fs.readdirSync(directory).forEach(function (dirInner) {
dirInner = path.resolve(directory, dirInner);
var stat = fs.statSync(dirInner);
console.log(stat)
if (stat.isDirectory()) {
results = results.concat(findFile(dirInner, pattern))
if (stat.isFile() && dirInner.endsWith(pattern)) {
results.push(dirInner);
}
}
console.log(results)
return results;
})
})
//Output
[]
//If I try the following:
getArtifact().then(findFile(directory, file))
// I get same empty array
[]
//If I run "findFile" in its own script after the download extract I get the following:
[
'SOME_FILE_PATH\\document1',
'SOME_FILE_PATH\\document2',
'SOME_FILE_PATH\\document3',
'SOME_FILE_PATH\\document4',
'SOME_FILE_PATH\\document5',
'SOME_FILE_PATH\\document6',
'SOME_FILE_PATH\\document7
]
Any help with how I can incorporate my findFile function into my existing download&extract function is appreciated...
I wanted to delete the file from storage when a data node is deleted from the realtime database. the name of the file to be deleted is taken before deleted. The name of the file is saved in the node in child named "imageTitle". The code works fine before implementing the file delete code. I mean the nodes get deleted perfectly.
When I implement the file delete code the rest doesn't work, but there is no any errors. The code after file delete doesn't work. I dunno why.
This is for an academic final project.
There's a folder named images in the bucket, and the file I need to delete is in there. The file name is taken from the child in the node which is to be deleted in the realtime database named imageTitle:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.changetime = functions.database.ref('/posts/{pushId}')
.onCreate((snapshot, context) => {
const editDate = Date.now()
datas = snapshot.val();
return snapshot.ref.update({editDate})
})
const CUT_OFF_TIME = 1 * 60 * 1000;
/**
* This database triggered function will check for child nodes that are older than the
* cut-off time. Each child needs to have a `timestamp` attribute.
*/
exports.deleteOldItems = functions.database.ref('/posts/{pushId}').onWrite(async (change,
context) => {
const ref = change.after.ref.parent; // reference to the parent
const now = Date.now();
const id = context.params.pushId;
const cutoff = now - CUT_OFF_TIME;
const oldItemsQuery = ref.orderByChild('createdDate').endAt(cutoff);
const snapshot = await oldItemsQuery.once('value');
const getImageTitle = admin.database().ref(`/posts/${id}/imageTitle`).once('value');
return getImageTitle.then(imageTitle => {
console.log('post author id', imageTitle.val());
const imageName = imageTitle.val();
const filePath = 'images/' + imageName;
const path = `images/${imageName}`;
const bucket = app.storage().bucket();
return bucket.file(path).delete().then(() =>{
console.log(`File deleted successfully in path: ${imageName}`)
/* The problem is here. the code doesn't work after file.delete function. no errors. but doesn't work
if I remove that piece of code the other codes work fine. I mean the realtime database nodes get deleted and updated perfectly */
const updates = {};
snapshot.forEach(child => {
updates[child.key] = null;
if(updates[child.key] == null){
admin.database().ref(/post-likes/+id).remove();
}
})
return ref.update(updates);
});
});
[my storage looks like this][1]});
There's a folder named images in the bucket, and the file I need to delete is in there. The file name is taken from the child in the node which id to be deleted in the realtime database imageTitle:
enter image description here
I think this is the problem:
const filePath = 'images/' + imageName;
const path = `images/${imageName}`;
You should be using filePath not path in your bucket reference. const path = `images/${imageName}`; is wild-card syntax used when querying, not when assigning variables....think you stayed in 'Dart' mode here:-). Not sure what path contains, therefore, but console.log it to see.
Here is some code that I use to delete images from my storage bucket that works perfectly. First, a couple of things to check (best done using console.log, never assume you know what is in the variable), and do:
Ensure imageName, filePath, path and bucket contain what they should contain.
Include the .catch block in your code to check if there actually is an error
I am not clear if console.log(`File deleted successfully in path: ${imageName}`) is executing but if it isn't then your file.delete must be throwing an error which the catch block should trap.
The code snippet:
const oldProfileImagePath = "profileImages/" + authenticatedUserId + "/" + oldProfileImageName;
return bucket.file(oldProfileImagePath).delete()
.then(function () {
console.info("Old Profile Image: " + oldProfileImagePath + " deleted!");
})
.catch(function (error) {
console.error("Remove Old Profile Image: " + oldProfileImagePath +
" failed with " + error.message)
});
in my app user can have multiple directory in setting , I want to get one file for each folder, note these directories can have their own directories an nested as possible
my config.json
{"dirs":["F:/music/Ellie Goulding","F:/music/Eminem - Revival [Album] [iTunes Version] - [7tunes]"]}
var findSongs = async function (baseDir,isDir=false,musics) {
try{
musics =musics || []
var files = await getDirFiles(baseDir)
for(let i =0;i<files.length;i++){
try {
let file = files[i]
var musicPath = `${baseDir}/${file}`
let stat = fs.lstatSync(musicPath)
if(stat.isDirectory()){
await findSongs(musicPath,true,musics)
}else{
let meta = await getMusicMeta(musicPath)
if(meta){
musics.push(meta)
}
if(isDir){
break
}
}
issue with this code is it goes get all songs of second folder and doesn't return anything from folder one but if only place one folder like this : F:/music it will work as expected
I want to create a scraper that:
opens a headless browser,
goes to a url,
logs in (there is steam oauth),
fills some inputs,
and clicks 2 buttons.
My problem is that every new instance of headless browser clears my login session, and then I need to login again and again...
How to save it through instances? (using puppeteer with headless chrome)
Or how can I open already logged in chrome headless instance? (if I have already logged in in my main chrome window)
There is an option to save user data using the userDataDir option when launching puppeteer. This stores the session and other things related to launching chrome.
puppeteer.launch({
userDataDir: "./user_data"
});
It doesn't go into great detail but here's a link to the docs for it: https://pptr.dev/#?product=Puppeteer&version=v1.6.1&show=api-puppeteerlaunchoptions
In puppeter you have access to the session cookies through page.cookies().
So once you log in, you could get every cookie and save it in a json file:
const fs = require(fs);
const cookiesFilePath = 'cookies.json';
// Save Session Cookies
const cookiesObject = await page.cookies()
// Write cookies to temp file to be used in other profile pages
fs.writeFile(cookiesFilePath, JSON.stringify(cookiesObject),
function(err) {
if (err) {
console.log('The file could not be written.', err)
}
console.log('Session has been successfully saved')
})
Then, on your next iteration right before using page.goto() you can call page.setCookie() to load the cookies from the file one by one:
const previousSession = fs.existsSync(cookiesFilePath)
if (previousSession) {
// If file exist load the cookies
const cookiesString = fs.readFileSync(cookiesFilePath);
const parsedCookies = JSON.parse(cookiesString);
if (parsedCookies.length !== 0) {
for (let cookie of parsedCookies) {
await page.setCookie(cookie)
}
console.log('Session has been loaded in the browser')
}
}
Checkout the docs:
https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagecookiesurls
https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagesetcookiecookies
For a version of the above solution that actually works and doesn't rely on jsonfile (instead using the more standard fs) check this out:
Setup:
const fs = require('fs');
const cookiesPath = "cookies.txt";
Reading the cookies (put this code first):
// If the cookies file exists, read the cookies.
const previousSession = fs.existsSync(cookiesPath)
if (previousSession) {
const content = fs.readFileSync(cookiesPath);
const cookiesArr = JSON.parse(content);
if (cookiesArr.length !== 0) {
for (let cookie of cookiesArr) {
await page.setCookie(cookie)
}
console.log('Session has been loaded in the browser')
}
}
Writing the cookies:
// Write Cookies
const cookiesObject = await page.cookies()
fs.writeFileSync(cookiesPath, JSON.stringify(cookiesObject));
console.log('Session has been saved to ' + cookiesPath);
For writing Cookies
async function writingCookies() {
const cookieArray = require(C.cookieFile); //C.cookieFile can be replaced by ('./filename.json')
await page.setCookie(...cookieArray);
await page.cookies(C.feedUrl); //C.url can be ('https://example.com')
}
For reading Cookies, for this, you've to install jsonfile in your project : npm install jsonfile
async function getCookies() {
const cookiesObject = await page.cookies();
jsonfile.writeFile('linkedinCookies.json', cookiesObject, { spaces: 2 },
function (err) {
if (err) {
console.log('The Cookie file could not be written.', err);
}
console.log("Cookie file has been successfully saved in current working Directory : '" + process.cwd() + "'");
})
}
Call these two functions using await and it will work for you.