R
Railway•8mo ago
RUNNb

How to use volume with nextjs?

I am trying to persist image on volume. RAILWAY_VOLUME_MOUNT_PATH =/images I've tried to implement the change for the railway volume but without success. This is the api route for local storage on /public/upload_images
import { NextApiHandler, NextApiRequest } from "next";
import formidable from "formidable";
import path from "path";
import fs from "fs/promises";
import {randomUUID} from "crypto";

export const config = {
api: {
bodyParser: false,
},
};

const readFile = (
req: NextApiRequest,
filename: string,
saveLocally?: boolean
): Promise<{ fields: formidable.Fields; files: formidable.Files }> => {
const options: formidable.Options = {};
if (saveLocally) {
options.uploadDir = path.join(process.cwd(), "/public/upload_images");
options.filename = (name, ext, path, form) => {
const extension = path.originalFilename?.split(".")[1];
return `${filename}.${extension}`;
};
}
options.maxFileSize = 4000 * 1024 * 1024;
const form = formidable(options);
return new Promise((resolve, reject) => {
form.parse(req, (err, fields, files) => {
if (err) reject(err);
resolve({ fields, files });
});
});
};
const handler: NextApiHandler = async (req, res) => {
try {
// await fs.readdir(path.join(process.cwd() + "/public", "/upload_images"));
await fs.readdir(path.join(process.cwd() + "/public", "/upload_images"));
} catch (error) {
// await fs.mkdir(path.join(process.cwd() + "/public", "/upload_images"));
await fs.mkdir(path.join(process.cwd() + "/public", "/upload_images"));
}

const filename = randomUUID();
const { files } = await readFile(req, filename, true);
// @ts-ignore
const extension = files.image[0]?.newFilename.split('.')[1];
res.json({ done: "ok", filename: `${filename}.${extension}` });
};
export default handler;
import { NextApiHandler, NextApiRequest } from "next";
import formidable from "formidable";
import path from "path";
import fs from "fs/promises";
import {randomUUID} from "crypto";

export const config = {
api: {
bodyParser: false,
},
};

const readFile = (
req: NextApiRequest,
filename: string,
saveLocally?: boolean
): Promise<{ fields: formidable.Fields; files: formidable.Files }> => {
const options: formidable.Options = {};
if (saveLocally) {
options.uploadDir = path.join(process.cwd(), "/public/upload_images");
options.filename = (name, ext, path, form) => {
const extension = path.originalFilename?.split(".")[1];
return `${filename}.${extension}`;
};
}
options.maxFileSize = 4000 * 1024 * 1024;
const form = formidable(options);
return new Promise((resolve, reject) => {
form.parse(req, (err, fields, files) => {
if (err) reject(err);
resolve({ fields, files });
});
});
};
const handler: NextApiHandler = async (req, res) => {
try {
// await fs.readdir(path.join(process.cwd() + "/public", "/upload_images"));
await fs.readdir(path.join(process.cwd() + "/public", "/upload_images"));
} catch (error) {
// await fs.mkdir(path.join(process.cwd() + "/public", "/upload_images"));
await fs.mkdir(path.join(process.cwd() + "/public", "/upload_images"));
}

const filename = randomUUID();
const { files } = await readFile(req, filename, true);
// @ts-ignore
const extension = files.image[0]?.newFilename.split('.')[1];
res.json({ done: "ok", filename: `${filename}.${extension}` });
};
export default handler;
Solution:
The file is being saved now. I've also created an new endpoint to fetch the content from the volume, following the same approach. ```ts options.uploadDir = process.env.RAILWAY_VOLUME_MOUNT_PATH ? process.env.RAILWAY_VOLUME_MOUNT_PATH : path.join(process.cwd(), "/public/upload_images");...
Jump to solution
28 Replies
Percy
Percy•8mo ago
Project ID: e46bd0a0-4466-4489-a2d3-6355d062c901
RUNNb
RUNNb•8mo ago
e46bd0a0-4466-4489-a2d3-6355d062c901
Fragly
Fragly•8mo ago
where are you storing your images on /images ? <:mommy_confused:1023749002069540935> if your mount path is /images then you need to store the images there
RUNNb
RUNNb•8mo ago
I tried that already I've tried like this:
import { NextApiHandler, NextApiRequest } from "next";
import formidable from "formidable";
import path from "path";
import fs from "fs/promises";
import {randomUUID} from "crypto";

export const config = {
api: {
bodyParser: false,
},
};

const readFile = (
req: NextApiRequest,
filename: string,
saveLocally?: boolean
): Promise<{ fields: formidable.Fields; files: formidable.Files }> => {
const options: formidable.Options = {};
if (saveLocally) {
options.uploadDir = path.join(process.env.RAILWAY_VOLUME_MOUNT_PATH, "/uploads");
options.filename = (name, ext, path, form) => {
const extension = path.originalFilename?.split(".")[1];
return `${filename}.${extension}`;
};
}
options.maxFileSize = 4000 * 1024 * 1024;
const form = formidable(options);
return new Promise((resolve, reject) => {
form.parse(req, (err, fields, files) => {
if (err) reject(err);
resolve({ fields, files });
});
});
};
const handler: NextApiHandler = async (req, res) => {
try {
// await fs.readdir(path.join(process.cwd() + "/public", "/upload_images"));
await fs.readdir(path.join(process.env.RAILWAY_VOLUME_MOUNT_PATH, "/uploads"));
} catch (error) {
// await fs.mkdir(path.join(process.cwd() + "/public", "/upload_images"));
await fs.mkdir(path.join(process.env.RAILWAY_VOLUME_MOUNT_PATH, "/uploads"));
}

const filename = randomUUID();
const { files } = await readFile(req, filename, true);
// @ts-ignore
const extension = files.image[0]?.newFilename.split('.')[1];
res.json({ done: "ok", filename: `${filename}.${extension}` });
};
export default handler;
import { NextApiHandler, NextApiRequest } from "next";
import formidable from "formidable";
import path from "path";
import fs from "fs/promises";
import {randomUUID} from "crypto";

export const config = {
api: {
bodyParser: false,
},
};

const readFile = (
req: NextApiRequest,
filename: string,
saveLocally?: boolean
): Promise<{ fields: formidable.Fields; files: formidable.Files }> => {
const options: formidable.Options = {};
if (saveLocally) {
options.uploadDir = path.join(process.env.RAILWAY_VOLUME_MOUNT_PATH, "/uploads");
options.filename = (name, ext, path, form) => {
const extension = path.originalFilename?.split(".")[1];
return `${filename}.${extension}`;
};
}
options.maxFileSize = 4000 * 1024 * 1024;
const form = formidable(options);
return new Promise((resolve, reject) => {
form.parse(req, (err, fields, files) => {
if (err) reject(err);
resolve({ fields, files });
});
});
};
const handler: NextApiHandler = async (req, res) => {
try {
// await fs.readdir(path.join(process.cwd() + "/public", "/upload_images"));
await fs.readdir(path.join(process.env.RAILWAY_VOLUME_MOUNT_PATH, "/uploads"));
} catch (error) {
// await fs.mkdir(path.join(process.cwd() + "/public", "/upload_images"));
await fs.mkdir(path.join(process.env.RAILWAY_VOLUME_MOUNT_PATH, "/uploads"));
}

const filename = randomUUID();
const { files } = await readFile(req, filename, true);
// @ts-ignore
const extension = files.image[0]?.newFilename.split('.')[1];
res.json({ done: "ok", filename: `${filename}.${extension}` });
};
export default handler;
Fragly
Fragly•8mo ago
any errors?
RUNNb
RUNNb•8mo ago
Only when trying to access the image
RUNNb
RUNNb•8mo ago
No description
RUNNb
RUNNb•8mo ago
and disk usage didn't change
No description
Medim
Medim•8mo ago
why is your volume mount path on /images and the dir ur trying to access is /public/upload_images? isnt your volume path going to be the same?
RUNNb
RUNNb•8mo ago
I've tried to do that. I used the second snippet I've sent here
Medim
Medim•8mo ago
i remember someone having the same issue but i can't find the thread
RUNNb
RUNNb•8mo ago
I've been searching aswell but nothing about this :/
Medim
Medim•8mo ago
wild guess but try uploading at /app/RAILWAY_VOLUME_MOUNT_PATH/uploads
RUNNb
RUNNb•8mo ago
i'll try that
Medim
Medim•8mo ago
if that doesn't work we better wait for a specialist (brody)
RUNNb
RUNNb•8mo ago
No description
RUNNb
RUNNb•8mo ago
Ahahah, let me try without the /uploads see if it detects the path
Medim
Medim•8mo ago
dayum, it wasnt supposed to be this hard to use a volume storage lmao
RUNNb
RUNNb•8mo ago
xD
Solution
RUNNb
RUNNb•8mo ago
The file is being saved now. I've also created an new endpoint to fetch the content from the volume, following the same approach.
options.uploadDir = process.env.RAILWAY_VOLUME_MOUNT_PATH ?
process.env.RAILWAY_VOLUME_MOUNT_PATH : path.join(process.cwd(), "/public/upload_images");

const handler: NextApiHandler = async (req, res) => {
try {
// await fs.readdir(path.join(process.cwd() + "/public", "/upload_images"));
await fs.readdir(process.env.RAILWAY_VOLUME_MOUNT_PATH ?
process.env.RAILWAY_VOLUME_MOUNT_PATH :
path.join(process.cwd() + "/public", "/upload_images"));
} catch (error) {
console.log('directory error: ', error);
// await fs.mkdir(path.join(process.cwd() + "/public", "/upload_images"));
await fs.mkdir(
process.env.RAILWAY_VOLUME_MOUNT_PATH ?
process.env.RAILWAY_VOLUME_MOUNT_PATH :
path.join(process.cwd() + "/public", "/upload_images"));
}

try {
const filename = randomUUID();
const { files } = await readFile(req, filename, true);
// @ts-ignore
const extension = files.image[0]?.newFilename.split('.')[1];
res.json({ done: "ok", filename: `${filename}.${extension}` });
} catch (e) {
console.log('error: ', e);
res.json({ done: "nok" });
}
};

export default handler;
options.uploadDir = process.env.RAILWAY_VOLUME_MOUNT_PATH ?
process.env.RAILWAY_VOLUME_MOUNT_PATH : path.join(process.cwd(), "/public/upload_images");

const handler: NextApiHandler = async (req, res) => {
try {
// await fs.readdir(path.join(process.cwd() + "/public", "/upload_images"));
await fs.readdir(process.env.RAILWAY_VOLUME_MOUNT_PATH ?
process.env.RAILWAY_VOLUME_MOUNT_PATH :
path.join(process.cwd() + "/public", "/upload_images"));
} catch (error) {
console.log('directory error: ', error);
// await fs.mkdir(path.join(process.cwd() + "/public", "/upload_images"));
await fs.mkdir(
process.env.RAILWAY_VOLUME_MOUNT_PATH ?
process.env.RAILWAY_VOLUME_MOUNT_PATH :
path.join(process.cwd() + "/public", "/upload_images"));
}

try {
const filename = randomUUID();
const { files } = await readFile(req, filename, true);
// @ts-ignore
const extension = files.image[0]?.newFilename.split('.')[1];
res.json({ done: "ok", filename: `${filename}.${extension}` });
} catch (e) {
console.log('error: ', e);
res.json({ done: "nok" });
}
};

export default handler;
RUNNb
RUNNb•8mo ago
Ok, apparently the image is being saved. I've added a new endpoint to fetch the image content from the volume! If no futher info is needed, this ticket can be closed 🙂 thanks
Medim
Medim•8mo ago
you can close it by doing this
Brody
Brody•8mo ago
@RUNNb make sure the images are still there between deployments, it would be easy to save a file into the container and lose it on the next deployment
RUNNb
RUNNb•8mo ago
but seeing the disk usage change on the volume it would mean they are going to the volume right?
Medim
Medim•8mo ago
..not really, nextjs can use that volume too (idk if it does tho) better test redeploying ur service and seeing if they are still there
RUNNb
RUNNb•8mo ago
okok
Medim
Medim•8mo ago
also, be careful to not ping conductors or team #🛂|readme 5)
RUNNb
RUNNb•8mo ago
oh ok sorry