I am trying to implement IPFS in an application made with NodeJS. I can add file and get the CID, but if I try to download it with ipfs get <CID> it does not download anything. Any ideas what can be ?. I use this code to start an IPFS node:
const IPFS = require('ipfs');
// Spawn your IPFS node \o/
const node = new IPFS();
node.on('ready', () => {
node.id((err, id) => {
if (err) {
return console.log(err)
}
console.log(id)
})
let files = [
{
path: '/home/my/file.jpg',
content: File.read('/home/my/file.jpg', null)
}
]
node.files.add(files, function (err, files) {
if (err) {
console.log(err);
} else {
console.log(files)
}
})
})
When you start the node application, you will get some output like this:
[ { path: 'home/my/Pictures',
hash: '...hash',
size: 10329 },
{ path: 'home/my',
hash: '...hash',
size: 10384 },
{ path: 'home',
hash: '...hash',
size: 10435 },
{ path: '/home/my/Pictures/google.png',
hash: 'QmYeznhKxbZY37g5ymFzWnmrbP8ph2fdxycAuw9S9goUYr',
size: 10272 } ]
then copy that file hash, make sure you can access that file from the browser.
// replace the hash code for your own need
https://ipfs.io/ipfs/QmYeznhKxbZY37g5ymFzWnmrbP8ph2fdxycAuw9S9goUYr
then download the file in your terminal
// replace the hash code for your own need
ipfs get QmYeznhKxbZY37g5ymFzWnmrbP8ph2fdxycAuw9S9goUYr -o=google.png
Check here for more options
From nodejs you can do something like this provided necessary packages are imported:
const fileHash = 'you hash of the file you want to get'
ipfs.files.get(fileHash, function (err, files) {
files.forEach((file) => {
console.log(file.path)
console.log("File content >> ",file.content.toString('utf8'))
})
})
Related
I'm using react-native-mail in my React Native app. This is my code:
const path = RNFetchBlob.fs.dirs.CacheDir + '/' + SHA1(this.fileSource.uri) + '.pdf'
const self = this;
let data = this.fileSource.uri.replace(/data:application\/pdf;base64\,/i, '');
RNFetchBlob.fs
.writeFile(path, data, 'base64')
.then(() => {
Mailer.mail({
subject: "Subject",
body: "",
recipients: [this.props.emailAddress],
attachment: {
path: path,
type: 'pdf',
name: `attachment.pdf`,
}
}, (error) => {
...
});
})
.catch(() => {
...
})
The value of path is "/data/user/0/com.<my-app-name>/cache/5cae2ea1e235873729dd158e19f3d122a1b46c73.pdf"
The value of data is TIAoxIDAgb2JqIAo8PCAKL1R5cGUgL0NhdGFsb2cgCi9QYWdlcyAyIDAgUiAKL1BhZ2VNb... (very long)
The mail() method throws the error Failed to find configured root that contains /data/user/0/com.<my-app-name>/cache/5cae2ea1e235873729dd158e19f3d122a1b46c73.pdf
Android version: 11
react-native: 0.63.4
rn-fetch-blob: 0.12.0
react-native-mail: git+https://github.com/marcinolek/react-native-mail.git
Does anyone know how I can approach this?
Try changing the file location on an external path.
How do I get uploaded image in next.js API route and save it on public folder? I have front end ready. I'm uploading images to an endpoint using plain JavaScript.
here is the onSubmit function for uploading images. Suggest me if I'm doing it wrong here. The main question is how do I retrieve it?
const onSubmit=async(e)=>{
e.preventDefault();
const fd=new FormData()
fd.append('myfile',image.name)
let res=await fetch(`http://localhost:3000/api/upload`,{
method: 'POST',
headers: {
"Content-Type": "image/jpeg",
},
body: fd,
})
let response=await res.json();
one more bonus question, it's surely not a good idea to save the uploaded images on public folder. I have save it somewhere on the cloud.
This is the endpoint code I used for uploading image in nextjs, it requires some additional packages I will list them bellow also.
next-connect
multer
uuid
import nextConnect from "next-connect";
import multer from "multer";
import { v4 as uuidv4 } from "uuid";
let filename = uuidv4() + "-" + new Date().getTime();
const upload = multer({
storage: multer.diskStorage({
destination: "./public/uploads/profiles", // destination folder
filename: (req, file, cb) => cb(null, getFileName(file)),
}),
});
const getFileName = (file) => {
filename +=
"." +
file.originalname.substring(
file.originalname.lastIndexOf(".") + 1,
file.originalname.length
);
return filename;
};
const apiRoute = nextConnect({
onError(error, req, res) {
res
.status(501)
.json({ error: `Sorry something Happened! ${error.message}` });
},
onNoMatch(req, res) {
res.status(405).json({ error: `Method '${req.method}' Not Allowed` });
},
});
apiRoute.use(upload.array("file")); // attribute name you are sending the file by
apiRoute.post((req, res) => {
res.status(200).json({ data: `/uploads/profiles/${filename}` }); // response
});
export default apiRoute;
export const config = {
api: {
bodyParser: false, // Disallow body parsing, consume as stream
},
};
no Need to use any packages to handle file uploading you can use base64 to convert file to string and return it back to file by using "fs" module
why This way is beterr then using formData ?
because you duleing with normal post request where you can send any kind of data with it and use body parsere .
converting
const toBase64 = (file: File) => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
}
send a post request to server
const base64: string = await toBase64(file) as string;
const fileData = { base64, fileName: file.name };
const result = await api.post("/foo", fileData, name: "Salih", massage: "Hello World"})
converting base64 to file in server
function base64ToFile(file: { base64: string, fileName: string }) {
const fileContents = file.base64.replace(/^data:image\/png;base64,/, "");
fs.mkdirSync("./public/uploads", { recursive: true });
const fileName = `./public/uploads/${Date.now().toString() + file.fileName}`
fs.writeFile(fileName, fileContents, 'base64', function (err) { console.log(err) });
}
I suggest the popular and lightweight formidable library:
# install
yarn add formidable#v3 #types/formidable
// pages/api/file-upload.ts
import fs from "fs";
import path from "path";
import { File } from "formidable";
// Important for NextJS!
export const config = {
api: {
bodyParser: false,
},
};
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<string>
) {
try {
// Parse request with formidable
const { fields, files } = await parseFormAsync(req);
// Files are always arrays (formidable v3+)
const myfile = (files["myfile"] as any as File[])[0];
// Save file in the public folder
saveFile(myfile, "./public/uploads");
// Return success
res.status(200).json("success!");
} catch (e) {
return res.status(500).json(e);
}
}
function saveFile(file: File, publicFolder: string): void {
const fileExt = path.extname(file.originalFilename || "");
fs.renameSync(file.filepath, `${publicFolder}/${file.newFilename}${fileExt}`);
}
// ./helpers/formidable.ts
import type { NextApiRequest } from "next";
import formidable from "formidable";
export type FormidableParseReturn = {
fields: formidable.Fields;
files: formidable.Files;
};
export async function parseFormAsync(
req: NextApiRequest,
formidableOptions?: formidable.Options
): Promise<FormidableParseReturn> {
const form = formidable(formidableOptions);
return await new Promise<FormidableParseReturn>((resolve, reject) => {
form.parse(req, async (err, fields, files) => {
if (err) {
reject(err);
}
resolve({ fields, files });
});
});
}
Bonus question
one more bonus question, it's surely not a good idea to save the uploaded images on public folder. I have save it somewhere on the cloud.
S3 and other cloud services
You can save on cloud services with Formidable.
See the official examples: https://github.com/node-formidable/formidable/blob/master/examples/store-files-on-s3.js
But you don't need to use cloud storage to protect private uploads. You can store them locally.
Working with private uploads locally
Saving:
Store the uploads in a non-public folder;
Ex. /private-uploads/{logged_user_id}/;
Reading:
Create an API page to fetch the file
Ex. https://.../uploads/{filename}
Fail if the file doesn't belong to the authenticated user;
Send the file as the response;
Security:
With the above folder scheme, hackers can use .. and similar on the filename to obtain unauthorized access;
Sanitize the filename having this in mind (ex. only allow alphanumeric characters);
Alternatively, use a database table to control ownership instead of a folder scheme;
noob question, I'm just getting started with Google Drive API v3. How can I download dynamic file from google drive when I only have fileId. file can be, image, pdf, or docs.
I tried searching but I couldn't found any reference or example related to this.
This what I have so far but it only download specific file extension.
downloadFile(req, res) {
const auth = new google.auth.JWT(
client_email,
null,
private_key,
SCOPES,
);
const { fileId } = req.params;
const drive = google.drive({ version: 'v3', auth});
var dest = fs.createWriteStream('./tmp/downloads/dummy.pdf')
drive.files.get({
fileId,
alt: 'media',
}, {
responseType: 'stream'
}).then((driveResponse) => {
driveResponse.data.on('end', () => {
console.log(`downloading fileID ${fileId}`);
})
.on('error', (err) => {
console.log(err);
})
.on('data', (d) => {
console.log(d);
})
.pipe(dest)
})
.catch((err) => {
console.log(err);
})
}
Is there way to download dynamic files from google drive?
I believe your goal as follows.
You want to download the files from Google Drive using the service account and the file ID.
The files include both Google Docs files and the files except for Google Docs files.
You want to achieve this using googleapis for Node.js.
Modification points:
Unfortunately, from it only download specific file extension., I cannot understand about the detail of your situation. But I guess that the reason of your issue might be due to downloading both Google Docs files and the files except for Google Docs files.
When Google Docs files are downloaded, the files are required to be downloaded using the method of "Files: export" in Drive API.
When the files except for Google Docs files are downloaded, the files are required to be downloaded using the method of "Files: get" in Drive API.
I thought that above situation might be the reason of your issue.
In order to download both Google Docs files and the files except for Google Docs files, I propose the following flow.
Check the mimeType of the file ID.
Download the file using each method by the mimeType.
When above points are reflected to your script, it becomes as follows.
Modified script:
From:
var dest = fs.createWriteStream('./tmp/downloads/dummy.pdf')
drive.files.get({
fileId,
alt: 'media',
}, {
responseType: 'stream'
}).then((driveResponse) => {
driveResponse.data.on('end', () => {
console.log(`downloading fileID ${fileId}`);
})
.on('error', (err) => {
console.log(err);
})
.on('data', (d) => {
console.log(d);
})
.pipe(dest)
})
.catch((err) => {
console.log(err);
})
To:
drive.files.get({ fileId, fields: "*" }, async (err, { data }) => {
if (err) {
console.log(err);
return;
}
let filename = data.name;
const mimeType = data.mimeType;
let res;
if (mimeType.includes("application/vnd.google-apps")) {
const convertMimeTypes = {
"application/vnd.google-apps.document": {
type:
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
ext: ".docx",
},
"application/vnd.google-apps.spreadsheet": {
type:
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
ext: ".xlsx",
},
"application/vnd.google-apps.presentation": {
type:
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
ext: ".pptx",
},
};
filename += convertMimeTypes[mimeType].ext;
res = await drive.files.export(
{
fileId,
mimeType: convertMimeTypes[mimeType].type,
},
{ responseType: "stream" }
);
} else {
res = await drive.files.get(
{
fileId,
alt: "media",
},
{ responseType: "stream" }
);
}
const dest = fs.createWriteStream(filename);
res.data
.on("end", () => console.log("Done."))
.on("error", (err) => {
console.log(err);
return process.exit();
})
.pipe(dest);
});
Note:
In this modification, I prepared 3 types of Google Docs files at convertMimeTypes. When you want to download other mimeTypes, please modify convertMimeTypes. In this case, for example, Google Docs files are downloaded as Microsoft Docs files.
References:
Download files
Files: get
Files: export
I am trying to upload a file from mobile to google bucket using ionic 4. Although a file can upload into the could. I am struggling to get the file properties out of file object.
Here is my method,
async selectAFile() {
const uploadFileDetails = {
name: '',
contentLength: '',
size: '',
type: '',
path: '',
};
this.fileChooser.open().then(uri => {
this.file.resolveLocalFilesystemUrl(uri).then(newUrl => {
let dirPath = newUrl.nativeURL;
const dirPathSegments = dirPath.split('/');
dirPathSegments.pop();
dirPath = dirPathSegments.join('/');
(<any>window).resolveLocalFileSystemURL(
newUrl.nativeURL,
function(fileEntry) {
uploadFileDetails.path = newUrl.nativeURL;
const file: any = getFileFromFileEntry(fileEntry);
//log 01
console.log({ file });
uploadFileDetails.size = file.size;
uploadFileDetails.name = `${newUrl.name
.split(':')
.pop()}.${file.type.split('/').pop()}`;
uploadFileDetails.type = file.type;
async function getFileFromFileEntry(fileEntry) {
try {
return await new Promise((resolve, reject) =>
fileEntry.file(resolve, reject)
);
} catch (err) {
console.log(err);
}
}
},
function(e) {
console.error(e);
}
);
});
});
// here uploadFileDetails is simller to what I declared at the top ;)
// I wan't this to be populated with file properties
// console.log(uploadFileDetails.name) --> //''
const uploadUrl = await this.getUploadUrl(uploadFileDetails);
const response: any = this.uploadFile(
uploadFileDetails,
uploadUrl
);
response
.then(function(success) {
console.log({ success });
this.presentToast('File uploaded successfully.');
this.loadFiles();
})
.catch(function(error) {
console.log({ error });
});
}
even though I can console.log the file in log 01. I am unable to get file properties like, size, name, type out of the resolveLocalFileSystemURL function. basically, I am unable to populate uploadFileDetails object. What am I doing wrong? Thank you in advance.
you actually need 4 Ionic Cordova plugins to upload a file after getting all the metadata of a file.
FileChooser
Opens the file picker on Android for the user to select a file, returns a file URI.
FilePath
This plugin allows you to resolve the native filesystem path for Android content URIs and is based on code in the aFileChooser library.
File
This plugin implements a File API allowing read/write access to files residing on the device.
File Trnafer
This plugin allows you to upload and download files.
getting the file's metadata.
file.resolveLocalFilesystemUrl with fileEntry.file give you all the metadata you need, except the file name. There is a property called name in the metadata but it always contains value content.
To get the human readable file name you need filePath. But remember you can't use returning file path to retrieve metadata. For that, you need the original url from fileChooser.
filePathUrl.substring(filePathUrl.lastIndexOf('/') + 1) is used to get only file name from filePath.
You need nativeURL of the file in order to upload it. Using file path returning from filePath is not going to work.
getFileInfo(): Promise<any> {
return this.fileChooser.open().then(fileURI => {
return this.filePath.resolveNativePath(fileURI).then(filePathUrl => {
return this.file
.resolveLocalFilesystemUrl(fileURI)
.then((fileEntry: any) => {
return new Promise((resolve, reject) => {
fileEntry.file(
meta =>
resolve({
nativeURL: fileEntry.nativeURL,
fileNameFromPath: filePathUrl.substring(filePathUrl.lastIndexOf('/') + 1),
...meta,
}),
error => reject(error)
);
});
});
});
});
}
select a file from the file system of the mobile.
async selectAFile() {
this.getFileInfo()
.then(async fileMeta => {
//get the upload
const uploadUrl = await this.getUploadUrl(fileMeta);
const response: Promise < any > = this.uploadFile(
fileMeta,
uploadUrl
);
response
.then(function(success) {
//upload success message
})
.catch(function(error) {
//upload error message
});
})
.catch(error => {
//something wrong with getting file infomation
});
}
uploading selected file.
This depends on your backend implementation. This is how to use File Transfer to upload a file.
uploadFile(fileMeta, uploadUrl) {
const options: FileUploadOptions = {
fileKey: 'file',
fileName: fileMeta.fileNameFromPath,
headers: {
'Content-Length': fileMeta.size,
'Content-Type': fileMeta.type,
},
httpMethod: 'PUT',
mimeType: fileMeta.type,
};
const fileTransfer: FileTransferObject = this.transfer.create();
return fileTransfer.upload(file.path, uploadUrl, options);
}
hope it helps. :)
I wrote a Hapi.js route to receive an uploaded file and have called it successfully using Postman. Now I want to save the file.
How do I
get the file extension?
save the file to disk?
Here's my route:
{
method: 'POST',
path: this.config.apiPrefix + 'uploadprofilephoto',
config: { payload: { maxBytes: 10485760, /* 10 MB */ output: 'stream', parse: true } },
handler: (request: hapi.Request, reply: hapi.IReply) => {
const result = new Promise<string>( async (resolve, reject) => {
try {
this.profilePhotoRouteHelper.savePhotoAndUploadToAws(jwtData.userId, request.payload['image']);
resolve(responseHelper.getSuccessResponse<string>(null, newJwt));
}
catch (error) {
log.error(error);
resolve(responseHelper.getErrorResponse(ResponseErrorCode.unknownError));
}
});
reply(result);
}
and an idea of how to save:
fs.writeFile(filename, data, [encoding], () => { } );
but I'd rather use promises and await if possible.
Here's the uploaded file:
I found fs-promise, which works well.
const photoId = uuid.v4();
await fsp.writeFile(photoId + '__' + image.hapi.filename, image._data, 'utf8');
And here's how to get file extensions: Node.js get file extension