I'm doing an API for a gallery; so, I create a method that let copy an image from the database.
Now, I want to add a number at the end of the copy-image name. For example:
-original image name: image
-copy image name: image(1)
-2nd copy image name: image(2)
How can I add the number to the name of copied name automatically?
'use strict'
let imageObject= require('../models/image-object');
let fs=require('fs');
let path= require('path');
let gallery_controllers={
copyImage:function(req,res){
//get the id param of the image to copy
let imageId=req.params.id;
if(imageId==null) return res.status(404).send({message:"no ID defined"});
//I search the requiere image on the database
imageObject.findById(imageId,(err,image)=>{
if(err) return res.status(500).send({message:'err to response data'});
if(!image) return res.status(404).send({message:'image not found'});
if(image){
//set a new model-object
let imageCopied= new imageObject();
imageCopied.name= image.name;
imageCopied.image=image.image;
//save image copied on the database
imageCopied.save((err,image_copied)=>{
if(err) return res.status(500).send({message:"error 500"});
if(!image_copied) return res.status(404).send({message:"error 404"});
return res.status(200).send({
image:image_copied
})
})
}
})
},
}
Here's a function that looks in the directory passed to it for files of the name file(nnn) where nnn is some sequence of digits and returns back to you the full path of the next one in sequence (the one after the highest number that already exists).
This function pre-creates a placeholder file by that name to avoid concurrency issues with multiple asynchronous operations calling this function and potentially conflicting (if it only returned the filename without creating the file). To further handle conflicts, it creates the placeholder file in a mode that fails if it already exists (so only one invocation of this function will ever create that particular file) and it automatically retries to find a new number if it gets a conflict (e.g. someone else created the next available file before we got to it). All of this logic is to avoid the subtleties of possible race conditions in creating the next filename in the sequence.
Once the caller has a unique filename that this resolves to, then it is expected that they will overwrite the placeholder contents with their own contents.
// pass directory to look in
// pass base file name so it will look for next in sequence as in "file(3)"
// returns the full path of the unique placeholder file it has created
// the caller is then responsible for that file
// calling multiple times will create a new placeholder file each time
async function findNextName(dir, base) {
let cntr = 0;
const cntr_max = 5;
const regex = new RegExp(`^${base}\\((\\d+)\\)$`);
async function run() {
const files = await fs.promises.readdir(dir);
let highest = 0;
for (let f of files) {
let matches = f.match(regex);
if (matches) {
let num = parseInt(matches[1]);
if (num > highest) {
highest = num;
}
}
}
let name = `${base}(${highest + 1})`;
// create placeholder file with this name to avoid concurrency issues
// of another request also trying to use the same next file
try {
// write to file, fail if the file exists due to a concurrency issue
const fullPath = path.resolve(path.join(dir, name));
await fs.promises.writeFile(fullPath, "placeholder", { flag: "wx" });
return fullPath;
} catch (e) {
// if this fails because of a potential concurrency issue, then try again
// up to cntr_max times to avoid looping forever on a persistent error
if (++cntr < cntr_max) {
return run();
} else {
throw e;
}
}
}
return run();
}
You could call it like this:
findNextName(".", "file").then(filename=> {
console.log(filename);
}).catch(err => {
console.log(err);
});
Related
I've a image input in my webpage and input's output (File object) is saved inside the Question class. questionArr is a array of Question objects
let questionsArr = []; // Array of Question
class Question {
constructor(id) {
this.id = id;
this.image = false;
}
}
when the input value of image input changes, following function calls.
const handleImages = evt => {
let id = evt.target.id; // quizCoverImg or a integer (0,1,...)
const file = evt.target.files[0];
if (file && file.type.startsWith("image/")) {
if (id == "quizCoverImg") {
coverImage = file; // declared in top of the code
// console.log(coverImage) => File {name: "cat.png", lastModified ...}
// Returns a file object, which is correct
} else {
questionsArr[id].image = file;
// console.log(questionsArr[id].image) => File {name: "cat.png", lastModified ...}
// Returns a file object, which is correct
}
}
};
To this point everything works fine. Problem arise when I use above variables somewhere eles
const somewhereElse = () => {
console.log(coverImage); // File {name: "cat.png", lastModified ...} ✔
console.log(typeof coverImage); // object ✔
console.log(questionsArr[0].image); // C:\fakepath\cat.jpg ❓ should return a file object as mentioned above
console.log(typeof questionsArr[0].image); // string ❓
}
I know FileReader() exist, but I want to figure out why I'm getting two different outputs here.
Issue occurred in svelte#3.22.2
Edit 1: Places where questionArr used
This add Question to array
const addQuestion = () => {
const q = new Question(n);
questionsArr = [...questionsArr, q]; // because I'm using svelte :)
n++;
};
Then used in above handleImage()
The key difference is in the toString() method that affects what you are looking at. There is not much context to help debug the details of exactly how you are running this and how you are inspecting the values.
When you have selected a file in a form file input, the browser converts the path to a "fakepath" so that while the script can access the selected file, it cannot learn about the user's directory structure. The filename/path is a reasonable default toString result when trying to inspect/print the file object.
I'm building a Discord bot which will allow users to create custom commands.
It works in this way, user enters "!addcommand !commandname:command value". The program will then split the string, and add !commandname:command value to a txt file. Whenever somebody types !commandname into discord, the bot will then output "command value" into chat.
The program is supposed to check if the new command exists whenever an if statement is triggered. However, this only seems to be checking the first time the program is run, which causes new commands to not be recognized unless the program is restarted.
Note:
Client.on listens to the channel, and contents are run every time someone says something in chat.
!addcommand appears to be functioning correctly, and I'm able to confirm the lines are being written to the file as intended.
I don't know what else to try.
Main file:
//Assume that requires etc are included
client.on('message', message => {
const pingCheck = message.content.charAt(0);
const commandCheck = message.content.split(" ");
if (pingCheck === "!") {
//Populates the list of custom commands. Must be done on every check, or new commands will not be recognized.
//Currently, this seems to only update once the service/program is restarted
var commandList = customCommands.returnPhrase();
//If the commandList object contains the correct key (stored in commandCheck[0]) such as !commandname, the bot will send the value "command value" as a string to the discord chat.
if (commandList.hasOwnProperty(commandCheck[0])) {
message.channel.send(commandList[commandCheck[0]]);
}
//If the key does not exist, the program then checks predefined commands. Other commands exist here, but for the purposes of this question I'll show only the !addcommand, which is used to create a new custom command.
else {
switch (commandCheck[0]) {
case "!addcommand":
//This checks that the command is formatted properly, "!commandname:commandvalue". If it does not start with ! or contain : somewhere in the string, it's probably an invalid format.
//Technically this still allows for a !:commandvalue format. I haven't implemented a check for this yet.
if (commandCheck[1].startsWith("!") && commandCheck[1].includes(":")) {
//While loop reconstructs the command key to be passed in, ignores slot 0 as this is the !addcommand
var gs = "";
var x = 1;
while (x < commandCheck.length) {
gs += gs +commandCheck[x] + " ";
x++;
}
gs = gs.slice(0,-1)+"\r\n"; //removes the last " " from the input string, and adds line-break
addCommands.addPhrase(gs);//passes reconstructed command to be added to commandlist.txt
message.channel.send("I have added " + commandCheck[1] + " to the command list.");
break;
}
default:
message.channel.send("I dont recognize that command.");
}
}
}
});
Module which adds commands:
const fs = require('fs');
var createCommand = {
addPhrase: function(x) {
fs.appendFile("commandlist.txt", x, function(err){
if(err) throw err;
console.log(err)
});
}
}
module.exports = createCommand;
Module which populates list of custom commands:
const fs = require('fs');
var commandFile = fs.readFileSync('commandlist.txt','utf8');
var dataSplit = commandFile.split("\r\n");
var readCommand = {
returnPhrase: function(){
var splitSplit = {};
var i = 0;
//populates splitSplit with keys and values based on text file
while (i<dataSplit.length){
var newKey = dataSplit[i].split(':');
splitSplit[newKey[0]] = newKey[1];
i++
};
return splitSplit;
},
};
module.exports = readCommand;
Better readability: https://repl.it/repls/DarkvioletDeafeningAutomaticparallelization
Expected: commandList is populated everytime if statement is triggered
Actual: commandList populates first time statement is triggered
You write to the file whenever a new command comes in, however you only read once from it when the server starts, so you won't keep track of changes (untill you restart the server which will read the file again). Now you could theoretically listen for filechanges and reload then, but that is overcomplicating things, the filesystem is not meant to achieve that. Instead, just keep your commands in an object and export some methods for adding / checking:
let commands = {};
// ¹
module.exports = {
addCommand(key, value) {
commands[key] = value;
// ²
},
getValue(key) {
return commands[key];
}
};
Now when you add a command, it directly gets added to the object, and that can then directly be read out.
Now as objects aren't persisted accross restarts, you will lose all commands then. But that is easy to fix: You could just reflect the object to a file whenever it updates, and then load it on every start. Instead of creating a custom format for that, I'd just use JSON. The code above can easily be extended:
// ¹
try {
commands = JSON.parse( fs.readFileSync("commands.txt") );
} catch(e) { /* ignore if file doesnt exist yet */ }
// ²
fs.writeFile("commands.txt", JSON.stringify(commands), err => {
if(err) console.error(err);
});
How I would write the bot:
const playerCommands = require("./commands.js");
const defaultCommands = {
addCommand(...pairs) {
let result = "";
for(const pair of pairs) {
const [name, value] = pair.split(":");
playerCommands.addCommand(name, value);
result += `${name} added with value ${value}\n`;
}
return result;
}
};
client.on('message', ({ content, channel }) => {
if(content[0] !== "!") return; // ignore non-commandd
const [commandName, ...args] = content.slice(1).split(" ");
if(defaultCommands[commandName])
return channel.send(defaultCommands[commandName](...args));
const value = playerCommands.getValue(commandName);
if(value) return channel.send(value);
return channel.send("Command not found!");
});
I looked at lot of example but couldn't achieve it..so need help..
Problem..
the content from loop should be passed to execute one by one.
each loop iteration contains a file read and database save operation along with few other object properties that need to be assigned.
I have created example here..
http://runnable.com/VI1efZDJvlQ75mlW/api-promise-loop-for-node-js-and-hello-world
how to run:
Api: http://web-91b5a8f5-67af-4ffd-9a32-54a50b10fce3.runnable.com/api/upload
method : POST
content-type : multipart/form-data
upload more than one file with name.
..
the final expected promise is
files.name = "name of file"
files.content
files.content-type
files.size
- saved to db.
currently i am getting different content from file..but other files content are not filled and is undefined.
Regards
Moyeen
The technique you're looking for is thenable chaining
var p= Q();
Object.keys(files).forEach(function(key){
p = p.then(function(){ // chain the next one
return Q.nfcall(fs.readFile, files[key].path, "binary", i). // readfile
then(function (content) { // process content and save
files.filename = files[key].name;
files.path = files[key].path;
files.content_type = files[key].type;
files.size = files[key].size;
console.log(files.filename);
files.content = binaryToBase64(content);
return Q.npost(art.save, art); // wait for save, update as needed
}));
});
});
Basically, we tell each operation to happen after the previous one has finished by chaining them and returning which causes a wait on the asynchronous value.
As a byproduct you can later use
p.then(function(last){
// all done, access last here
});
The handler will run when all the promises are done.
I have updated the code with Q.all as the mentioned p.then will execute only once.
http://runnable.com/VI1efZDJvlQ75mlW/api-promise-loop-for-node-js-and-hello-world
form.parse(req, function(err, fields, files) {
var p = Q();
Object.keys(files).forEach(function (key) {
promises.push(p.then(function () { // chain the next one
return Q.nfcall(fs.readFile, files[key].path, "binary"). // readfile
then(function (content) { // process content and save
file = {};
file.filename = files[key].name;
file.path = files[key].path;
file.content_type = files[key].type;
file.size = files[key].size;
console.log(files[key].name);
file.content = binaryToBase64(content);
filesarr.push(file);
// Q.npost(art.save, art); // wait for save, update as needed
})
}));
Q.all(promises);
});
});
the question is how to use q.npost if i have mongoose model files and want to save...?
I'm running a script on an apache webserver on a linux box. Based on the parameter I want to change the name of variable(or set it)
The idea is that humDev(lines 11 and 14) is named humDev21 for example. Where devId is the number 21 in this example.
My script looks like this:
function getHumDev(devId){
$.ajax({
async: false,
url: "/url" + devId,
success: function(result) {
var array = result["Device_Num_" + devId].states;
function objectFindByKey(array, key, value) {
for (var i = 0; i < array.length; i++) {
if (array[i][key] === value) {
humDev = array[i].value;
}
}
return humDev;
};
objectFindByKey(array, 'service', 'some');
}
});
};
If Im looking in the wrong direction, please do let me know. Maybe its bad practice what Im trying. The reason I want to have the object a unique name is because this function is called several times by another function, based on the content of an array. But when I have the humDev object named without the number suffix to make it unique, the content of the object is getting mixed up between the different calls.
I may be off base but I am making some assumptions based on what I understand of what you are trying to do.
First, you need to understand how to do file I/O in node.js. So lets start there:
var pathToFile, //set with file path string
fs = require('fs'), //require the file i/o module API
bunchOfHumDevs = {},
fileContents; //we'll cache those here for repeated use
fs.readFile(pathToFile, function(err, result) {
if (err) {
throw new Error(); //or however you want to handle errors
} else {
fileContents = JSON.parse(result); //assumes data stored as JSON
}
});
function getHumDev(devId) {
//first make sure we have fileContents, if not try again in 500ms
if (!fileContents) {
setTimeout(function() {
getHumDev(devId);
}, 500);
} else {
var array = fileContents["Device_Num_" + devId].states,
i = array.length,
//if 'service' and 'some' are variable, make them params of
//getHumDev()
while (i--) {
if (array[i]['service'] === 'some') {
//store uniquely named humDev entry
bunchOfHumDevs['humDev' + devId.toString()] = array[i].value;
break; //exit loop once a match is found
}
}
}
return null;
}
getHumDev(21);
assuming a match is found for the devId 21, bunchOfHumdevs will now have a property 'humDev21' that is the object (value?) in question. Also, the fileContents are now cached in the program so you don't have to reopen it every time you call the function.
I am just getting started with coding for FirefoxOS and am trying to get a list of files in a directory.
The idea is to find the name of each file and add it to the array (which works), but I want to return the populated array and this is where I come unstuck. It seems that the array gets populated during the function (as I can get it to spit out file names from it) but when I want to return it to another function it appears to be empty?
Here is the function in question:
function getImageFromDevice (){
var imageHolder = new Array();
var pics = navigator.getDeviceStorage('pictures');
// Let's browse all the images available
var cursor = pics.enumerate();
var imageList = new Array();
var count = 0;
cursor.onsuccess = function () {
var file = this.result;
console.log("File found: " + file.name);
count = count +1;
// Once we found a file we check if there are other results
if (!this.done) {
imageHolder[count] = file.name;
// Then we move to the next result, which call the cursor
// success with the next file as result.
this.continue();
}
console.log("file in array: "+ imageHolder[count]);
// this shows the filename
}
cursor.onerror = function () {
console.warn("No file found: " + this.error);
}
return imageHolder;
}
Thanks for your help!
Enumerating over pictures is an asynchronous call. Essentially what is happening in your code is this:
You are initiating an empty array
You are are telling firefox os to look for pictures on the device
Then in cursor.onsuccess you are telling firefox os to append to the array you have created WHEN it gets back the file. The important thing here is that this does not happen right away, it happens at some point in the future.
Then you are returning the empty array you have created. It's empty because the onsuccess function hasn't actually happened.
After some point in time the onsuccess function will be called. One way to wait until the array is full populated would be to add in a check after:
if (!this.done) {
imageHolder[count] = file.name;
this.continue();
}
else {
//do something with the fully populated array
}
But then of course your code has to go inside the getImageFromDevice function. You can also pass a callback function into the getImageFromDevice function.
See Getting a better understanding of callback functions in JavaScript
The problem is with the aSynchronous nature of the calls you are using.
You are returning (and probably using) the value of imageHolder when it's still empty - as calls to the "onsuccess" function are deferred calls, they happen later in time, whereas your function returns immediately, with the (yet empty) imageHolder value.
You should be doing in this case something along those lines:
function getImageFromDevice (callback){
...
cursor.onsuccess = function () {
...
if (!this.done) {
// next picture
imageHolder[count] = file.name;
this.continue();
} else {
// no more pictures, return with the results
console.log("operation finished:");
callback(imageHolder);
}
}
}
Or use Promises in your code to accomplish the same.
Use the above by e.g.:
getImageFromDevice(function(result) {
console.log(result.length+" pictures found!");
});