What is the method for obtaining a ReadableStream from a GridFSBucket?

Can someone provide guidance on how to utilize the GridFSBucket stream within the streamFile function? Below is a functional example where a file on the disc is read and returned as a stream:

import { NextRequest, NextResponse } from "next/server";
import { ReadableOptions } from "stream";

function streamFile(path: string, options?: ReadableOptions): ReadableStream<Uint8Array> {
    const downloadStream = fs.createReadStream(path, options); // Replace this

    return new ReadableStream({
        start(controller) {
            downloadStream.on("data", (chunk: Buffer) => controller.enqueue(new Uint8Array(chunk)));
            downloadStream.on("end", () => controller.close());
            downloadStream.on("error", (error: NodeJS.ErrnoException) => controller.error(error));
        },
        cancel() {
            downloadStream.destroy();
        },
    });
}

export async function GET(req: NextRequest): Promise<NextResponse> {
    const file = req.nextUrl.searchParams.get("file");                                
    const stats: Stats = await fs.promises.stat(file);                              
    const data: ReadableStream<Uint8Array> = streamFile(file);                     
    const res = new NextResponse(data, {                                            
        status: 200,                                                                    
        headers: new Headers({                                                          
            "content-type": "video/mp4",                                            
            "content-length": stats.size + "",                                            
        }),
    });

    return res;                           
}

However, in my case, the data is stored in a mongoDB GridFSBucket. This is how I stream a video file directly from the GridFSBucket:

export const getServerSideProps: GetServerSideProps = async ({
    res,
    query: { hash }
}) => {        
    const database = await mongodb()
    const Videos = database.collection('videos')

    const { fileId } = await Videos.findOne({ uid: hash })

    const Files = new GridFSBucket(database)
    const id = new ObjectId(fileId)

    const file: GridFSFile = await new Promise((resolve, reject) => {
        Files.find({
            _id: id
        }).toArray((err, files) => {
            if (err) reject(err)
            resolve(files[0])
        })
    })
    const { contentType } = file || {}

    res.writeHead('Content-Type', contentType) // contentType is "video/mp4"
    Files.openDownloadStream(id)
    .on('data', (chunk) => {
        res.write(chunk)
    })
    .on('end', () => {
        res.end()
    })
    .on('error', (err) => {
        throw err
    })

    return {
        props: {}
    }
}

I am considering using the GridFSBucket instead of createReadStream for my task. However, the use of res.writeHead and openDownloadStream poses some challenges. How can I obtain a ReadableStream? Or is it necessary at all?

I aim to make this conversion because the getServerSideProps is being employed in an older nextJS application with pages router, and I need to transition to the new app router, hence the requirement for a server route component.

Answer №1

You may want to consider the following approach:

async function fetchFileFromGridFS(
  bucket: GridFSBucket,
  fileId: ObjectId,
  options?: GridFSBucketReadStreamOptions
): Promise<ReadableStream> {
  const downloadStream: GridFSBucketReadStream = bucket.openDownloadStream(fileId, options);
  //return new readable stream
}

Alternatively, you could simplify the process by utilizing the PassThrough stream provided by the stream module to transfer the data from the GridFSBucket to the response. Here's an example:

const passThrough = new PassThrough();
const downloadStream = Files.openDownloadStream(id);

downloadStream.pipe(passThrough);

return new NextResponse(passThrough, {
  status: 200,
  headers: {
    "Content-Type": contentType,
  },
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

having trouble connecting to mongodb atlas using mongoose

I'm in need of assistance! I've been attempting to connect to MongoDB Atlas using my connection string, but unfortunately, I am encountering some difficulties. I have a basic application set up and running, but I keep getting an error - specifica ...

Discovering the div element with a corresponding data attribute and subsequently removing a class from it

My code attempt doesn't seem to be functioning as expected: $('container').find("[data-slider='" + one + "']").removeClass('hidden'); This is the complete function enclosed within a document ready function: $("#service ...

When making recursive AJAX calls, the script that is included between each recursion is not being executed

My recursive Ajax call is functioning correctly (the PHP script is doing its job, recursion is working, everything is fine) EXCEPT that in between the ajax calls, I am trying to update an input text value to show the progress, but it only updates once the ...

Is there a way to integrate Prettify with Blogger/BlogSpot?

I am currently utilizing blogger.com as a platform to showcase programming texts and I am interested in incorporating Prettify (similar to Stack Overflow) to enhance the appearance of my code samples. What is the best method for installing the Prettify sc ...

Extracting Client's True IP Address using PHP API

Utilizing the following api: I am able to retrieve country, city, and real IP address in localhost (127.0.0.1) successfully. However, when I implement this code on my website, it displays the server's address instead of the client's. How can I r ...

Querying MongoDB for pulling/updating nested arrays in a paginated manner

In my database, I have a series of documents structured like this: Document1: { name: "tester1", reports: [{ name: "report1", type: "overflow" }, { name: "repor ...

Hey there everyone, I was wondering how to send both single and multiple values to a database using JavaScript and JSON endpoints with a Spring Web API

{ "id": 178, "stockin_date": "2022-11-15T08:18:54.252+00:00", "effective_date": null, "expired_date": null, "create_date": null, "update_date&q ...

Dynamically populate select options using Spring Boot Ajax technology

Hey everyone, I need some help with populating a select tag with a list of objects in JavaScript. Here's an example of the code: @RequestMapping(value="/getAllIndustries") @ResponseBody public List<IndustryModel>getAllIndustries() { return ...

Image broken despite accurate file path in next.js and tailwind

https://i.stack.imgur.com/2ZqPa.png In my components folder, specifically in navbar.js, I have a path to the image folder <img className="block lg:hidden h-8 w-auto" src='../images/logo_injoy.png' alt="Logo" /> U ...

Developing with AngularJS: Implementing Asynchronous Data Loading

Whenever I call a function, I encounter a Cannot read property of undefined error message, even though the data is being displayed correctly. From my understanding so far, this issue arises due to the asynchronous loading of data by AngularJS, and using p ...

The attention remains fixed at the top of the page

I have implemented an update panel along with pagination links using a repeater control at the bottom of my page. However, I am encountering an issue where clicking on the pagination links does not bring the page to the top. I attempted to use the followin ...

Modifications made at the highest level of the nextjs application will only impact the index page

My Bulma-powered next.js app includes a ServiceProvider. I have imported bulma and the provider at the top level of my application in the layout.js file. However, any changes made there do not persist. Upon navigating to a subpage, the css styling from b ...

The "main" entry for ts-node is not valid when running ts-node-dev

Recently, I embarked on a TypeScript project using yarn where I executed the following commands: yarn init -y yarn add typescript -D yarn tsc --init yarn add ts-node-dev -D Subsequently, I crafted a script titled dev that triggers tsnd src/index.ts, howev ...

Upon its second use, Ajax is loaded with cached data

Imagine a table with multiple rows, each row containing a SHOW button that reveals hidden content when clicked. The hidden div (with the classname "Content") loads the content of a second page using ajax and the id of the corresponding table row. However, ...

Vue.js / Nginx / Node.js - Error 413: Payload Too Big

My frontend is built using Vue.js and is hosted on an nginx server in production. Here's a snippet of my nginx.conf configuration: server { listen 80; server_name localhost; root /usr/share ...

Receiving no feedback from a public API while coding in React.js

I am currently delving into React.js and have been experimenting with a basic form. The issue I am facing is related to sending form data using ajax and not receiving a response. Upon running the code, the console displays an error message stating readysta ...

Add fresh inline designs to a React high-order component creation

Applying a common HOC pattern like this can be quite effective. However, there are instances where you may not want a component to be wrapped, but rather just extended. This is the challenge I am facing here. Wrapper HOC const flexboxContainerStyles = { ...

safely sending different form inputs in a Ruby on Rails form

Within this snippet, you'll find a part of a table with a form. Each row has its own submit button, which means users have to click on each one individually. I'm currently working on changing this so there is just one button for submission. < ...

Encountering an AxiosError while attempting to implement Stripe with Next.js

As a newcomer to Next.js, I've been navigating through bugs and challenges while working on an e-commerce platform. While most issues have been resolved, I'm stuck on one particular problem and could use some help. Currently, I am building the p ...

Updating and pushing data using Mongoose

I found this schema on a forum post about MongoDB here var mongoose = require('mongoose'); var ContactSchema = module.exports = new mongoose.Schema({ name: { type: String, required: true }, phone: { type: Number, required: ...