How to fetch file from mongodb gridfs using GraphQL? - mongodb

I completed file upload now I want to fetch file from mongodb using graphql. I have tried to fetch chunks of file and then merge it but it not worked as I expected.
So how to fetch entire file from mongodb inside graphql Query?

Here i'm returning bas64 as string of image which is not preferable for larger file size
const downloadFile = async (fileId) => {
const bucket = new mongoose.mongo.GridFSBucket(mongoose.connection.db, {
bucketName: 'files',
});
return new Promise((resolve, reject) => {
// temporary variable to hold image
var data = [];
// create the download stream
const readstream = bucket.openDownloadStream(new ObjectID(fileId));
readstream.on('data', function (chunk) {
data.push(chunk);
});
readstream.on('error', async (error) => {
reject(error);
});
readstream.on('end', async () => {
let bufferBase64 = Buffer.concat(data);
const img = bufferBase64.toString('base64');
resolve(img);
});
});
};
And this is query for fetching image
async getImage(_, { fileId }) {
try {
const img = await downloadFile(fileId);
return img;
} catch (error) {
throw new Error(error);
}
},

Related

How can I get Firebase Storage downloadable array of links and set it to object for storing in mongodb database?

My question is how do I set array of image links to my createAd function for storing the result into MongoDB database. Console log gives the getLinks result array as below. However, always I'm getting empty [] array for photos[] field in MongoDB database collection.
getlink function do the upload image to firestore and get downloadable url
const getLinks = (values) => {
const array = [];
values.adImgs.map((image: any) => {
const imgPath = `ad_images/${image.name + v4() }`;
const imageRef = ref(storage, imgPath);
uploadBytes(imageRef, image).then((snapshot) => {
getDownloadURL(snapshot.ref)
.then((url)=> array.push(url));
})
});
return array;
}
This is the function to store data into MongoDb database
const createAdd = async (values) => {
const newObj: any = {
title: values.title,
photos: getLinks(values)
}
await createPost(newObj);
}
The uploadBytes() and getDownloadURL() functions both return a promise. You are returning an empty before waiting for the files to upload. Try refactoring the code as shown below:
// async function
const getLinks = async (values) => {
const uploadPromises = values.adImgs.map((image) => {
const imgPath = `ad_images/${image.name + v4() }`;
const imageRef = ref(storage, imgPath);
return uploadBytes(imageRef, image);
})
// Upload all images
const res = await Promise.all(uploadPromises);
const links = await Promise.all(res.map((r) => getDownloadURL(r.ref)));
return links;
}
const createAdd = async (values) => {
const newObj: any = {
title: values.title,
photos: await getLinks(values) // <-- add await
}
await createPost(newObj);
}

Error while Uploading Image to Mongodb using Gridfs and Graphql

Im trying to upload an image to mogodb using graphql and gridfs. When trying to do i'm facing a error :
" JSON Parse error: Unexpected identifier "This" "
I'm not sure what I've done wrong in the code.
Can anyone help me figure out where I've gone wrong in the implementation
This is the part for uploading Image
const selectProfilePic = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [2, 3],
quality: 1,
});
handleImagePicked(result);
};
const handleImagePicked = async (result: ImagePicker.ImagePickerResult) => {
try {
if (result.cancelled) {
alert("Upload cancelled");
return;
} else {
console.log("In HERE ::: 76");
const lastIndex = result.uri.lastIndexOf("/") + 1;
console.log(result);
const file = new ReactNativeFile({
uri: result.uri,
name: result.uri.substring(lastIndex),
type: "image/png",
});
setAvatar(result.uri);
console.log(file); // This result is getting printed.
await singleUpload({ // I think the Upload function is not getting called.
variables: {
file,
},
});
}
} catch (e) {
console.log(e);
alert("Upload failed");
}
};
Resolver Function
singleUpload: async (_, { file }, context) => {
const {db, user, hhhhhh, gfs} = context;
console.log(gfs);
const res = uploadFn({ file },gfs);
console.log(res);
Apollo.tsx File
const uploadLink = createUploadLink({
uri: CLIENT_HTTP_URI,
});
// splitLink is defined here
export const client = new ApolloClient({
link: ApolloLink.from([authLink, splitLink]),
cache: new InMemoryCache(),
});
Have created StorageEngine.js File with this code
const storage = new GridFsStorage({
url: DB_URI,
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if(err) { return reject(err); }
const fileName = buf.toString('hex') + path.extname(file.originalname);
const fileInfo = {
fileName,
bucketName: 'uploads'
};
resolve(fileInfo);
});
});
}
});
const upload = multer({storage});
const uploadFn = async ({file}, bucket) => {
console.log(file);
const { createReadStream, name, type, encoding } = await file;
const uploadStream = bucket.openUploadStream(name, {
contentType: type
});
// console.log(uploadStream);
return new Promise((resolve, reject) => {
createReadStream()
.pipe(uploadStream)
.on('error', reject)
.on('finish', () => {
console.log(uploadStream.id);
resolve(uploadStream.id);
});
});
}

Can't fetch data with react-query

On my client-side(React js), I want to fetch data from the backend. But it's not working. The output of data is undefiend. Code Snippets
const url = `http://localhost:5000/items/${id}`;
const { data } = useQuery("data", () => axios(url));
console.log("data", data);
In the backend, I am using Express js and MongoDB as databases.
Try creating a function for fetching, for example:
const fetchData = async (id) => {
try {
const URL = `http://localhost:5000/items/${id}`;
const response = await axios.get(URL);
return response;
} catch(error) {
console.log(error);
}
}
Then:
const { data } = useQuery("data", () => fetchData(id));
You have to provide the id to fetchData

How to use axios to `fetch all()`

I don't understand how to use axios to fetch data from an array of urls. But I can do it with fetch. The following code works perfectly:
const url = 'https://vimeo.com/api/oembed.json?url='
async index(videoUrls = []) {
try {
const response = await Promise.all(
// videoUrls.map(videoUrl => axios.$get(`${url}${encodeURIComponent(videoUrl)}`))
videoUrls.map(videoUrl => fetch(`${url}${encodeURIComponent(videoUrl)}`))
)
const results = await Promise.all(response.map(r => r.json()));
return results;
} catch (e) {
console.error(e)
}
}
When I make a call like index(["https://vimeo.com/216850224", "https://vimeo.com/642263700"]), my console shows an array with all the video meta details vimeo has to give me. This is perfect.
But the moment I comment out the line that uses fetch and use axios, I get a CORS error.
What is the idiomatic way to fetch data from a bunch of urls in axios?
EDIT
I also tried this, but the .all() function doesn't seem to exist
async index(videoUrls = []) {
try {
const response = await axios.all(videoUrls.map(videoUrl => `${url}${encodeURIComponent(videoUrl)}`));
return response;
} catch (e) {
console.error(e)
}
}
You can easily do it like below:
(async function getAll() {
const axiosrequest1 = axios.get('https://jsonplaceholder.typicode.com/posts');
const axiosrequest2 = axios.get('https://jsonplaceholder.typicode.com/posts');
const axiosrequest3 = axios.get('https://jsonplaceholder.typicode.com/posts');
const [res1, res2, res3] = await Promise.all([axiosrequest1, axiosrequest2, axiosrequest3]);
console.log('request1', res1.data);
console.log('request2', res2.data);
console.log('request3', res3.data);
})();
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
The Axios version would be slightly different because it automatically decodes and embeds the response body into the response.data property (no need for res.json())
const baseUrl = "https://vimeo.com/api/oembed.json"
const index = async (videoUrls = []) => {
// create an array of responses and wait for them to resolve
const responses = await Promise.all(
videoUrls.map(url => axios.get(baseUrl, { params: { url } })
)
// extract the `data` properties and return them as an array
return responses.map(({ data }) => data)
}
Exactly when you extract response.data is totally up to you. It could also look like this
const index = (videoUrls = []) => Promise.all(
videoUrls.map(async (url) => (
await axios.get(baseUrl, { params: { url } })
).data)
)
FYI, your fetch() version could be a little cleaner too...
const baseUrl = "https://vimeo.com/api/oembed.json"
const index = (videoUrls = []) => Promise.all(
videoUrls.map(async (url) => {
const params = new URLSearchParams({ url })
const res = await fetch(`${baseUrl}?${params}`)
if (!res.ok) { // check for bad response
throw new Error(`${res.status}: ${await res.text()}`)
}
return res.json()
})
)

delete and create folder before uploading files into the folder using Multer

I am trying to upload files into a folder using multer and it is working fine.
Now my requirement is before it upload file into 'uploads' folder, it should delete it first, create the upload folder and then upload it.
I just want to do operation on uploaded file not on the previous data stored.
Code:
const fs = require("fs-extra");
const path = require("path");
const uploadPath = path.resolve(__dirname, "uploads");
const multer = require("multer");
const storage = multer.diskStorage({
destination: "./uploads/",
filename: function(req, file, cb) {
cb(null, file.originalname);
}
});
const upload = multer({ storage: storage });
router.post("/fileupload", upload.array("docs", 10), async function(
req,
res,
next
) {
let result = {};
try {
if (fs.existsSync(uploadPath)) {
fs.removeSync(uploadPath);
console.log("dir removed");
fs.ensureDirSync(uploadPath);
console.log("directory created");
} else {
fs.ensureDirSync(uploadPath);
console.log("directory created");
}
const uploadObj = util.promisify(upload.any());
await uploadObj(req, res);
result.message = "Upload successful";
res.send(result);
} catch (e) {
console.error(e);
console.error("Upload error");
}
});
I tried to make the code async also but after that it is not uploading any file. What I understood is upload.array is a middleware so it run first whenever POST request is called and rest run after this. So multer is uploading the data in existing folder and then once it comes inside the POST fs is deleting and creating it again.
how can I make it work?
Thanks
I found a way to make it work.
As multer is a middleware it executes first then the rest code. So i put that middleware in my code instead at the header. Below is the full code.
const create_upload_dir = () => {
if (fs.existsSync(uploadPath)) {
fs.removeSync(uploadPath);
console.log("dir removed");
fs.ensureDirSync(uploadPath);
console.log("directory created");
} else {
fs.ensureDirSync(uploadPath);
console.log("directory created");
}
return Promise.resolve("Success");
};
const multer = require("multer");
const upload_documents = () => {
const storage = multer.diskStorage({
destination: "./uploads/",
filename: function(req, file, cb) {
cb(null, file.originalname);
}
});
const upload = multer({ storage: storage });
return upload;
};
router.post("/fileupload", async function(req, res, next) {
let result = {};
try {
await create_upload_dir();
const upload = upload_documents();
upload.array("docs", 10);
const uploadObj = util.promisify(upload.any());
await uploadObj(req, res);
console.log("upload successful");
res.send("Upload successful");
} catch (e) {
console.error(e);
console.error("Upload error");
}
});
notice I put in try block
upload.array("docs", 10);
instead of
router.post("/fileupload",upload.array("docs", 10), async function(req, res, next)