0

How can I send a file from either route.ts or page.ts? The file could be located anywhere in the file-system.

I am currently using this code in python + flask in the back-end...

@app.route("/thumbnail/<string:filename>")
def get_file(filename):
    pathname = os.path.join(session['uploadpath'], filename)
    return send_file(pathname)

to deliver images to the browser - in response to the HTML+jinja code...

<img src="/thumbnail/{{file}}" style="height:40px"/>

This allowed me to deliver images from anywhere in the file-system.

Alternately, I am also able create an image in memory and send it as a file without having to save it.

@app.route("/textimg")
def get_file(filename):
    img = Image.new('RGB',(120,40), color='red')
    fnt = ImageFont.truetype(app.root_path + '/FONTNAME.TTF', 36)
    d = ImageDraw.Draw(img)
    d.text((10,0), '123456', font=fnt, fill=(255,255,0))

    img_io = BytesIO()
    img.save(img_io, 'JPEG', quality=70)
    img_io.seek(0)
    
    return send_file(img_io, mimetype='image/jpeg')

Now, I need to migrate my back-end. The client wants me to use only nextjs and am unable to find the equivalent of send_file. In nextjs, I am unable to get files from outside the public folder.

Please help me to resolve this problem - How can I send_file in nextjs?

I have tried...

    res.setHeader('Content-Type', 'image/jpg')
    const imageStream = createReadStream(file)
    pipeline(imageStream, res, (error) => {
        console.log(error);
    })

and

import fs from 'fs'
import path from 'path'

const filePath = path.resolve('.', 'images_folder/next.jpg')
const imageBuffer = fs.readFileSync(filePath)

export default function(req, res) {
  res.setHeader('Content-Type', 'image/jpg')
  res.send(imageBuffer)
}

and

import { ImageResponse } from "next/server";

export default async function get_file(req, res) {
  const filename = req.query.filename;
  const pathname = os.path.join(session['uploadpath'], filename);

  const imageResponse = new ImageResponse(pathname);
  imageResponse.headers.set("Content-Type", "image/png");

  return imageResponse;
}

Googling turned up these code-snippets but...

4
  • I'm not sure what pipeline is but did you try to use imageStream.pipe(res)? Commented Oct 4, 2023 at 13:13
  • What do you mean you aren't able to read files outside the /public directory? Next doesn't change how fs.readFile works...also "I've tried" isn't very helpful, tell us what happened when you tried that code. Commented Oct 4, 2023 at 14:31
  • 1
    Wouldn't be better to return the image URL instead? Commented Oct 4, 2023 at 14:47
  • stackoverflow.com/questions/63066985/… Commented Oct 4, 2023 at 15:45

1 Answer 1

-1

It is not recommended to send files from the server using Next.js because Next.js is a server-side rendered framework and the files should be served statically with the use of public folders. However, if you really need to send files from the server, you can use Express with Next.js to achieve this.

in root directory create server.js file

const express = require('express')
const next = require('next')
const multer = require('multer')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  const server = express()
  const upload = multer({ dest: 'uploads/' })

  server.post('/upload', upload.single('file'), (req, res) => {
    res.json({ file: req.file.filename })
  })

  server.get('/files/:filename', (req, res) => {
    const filename = req.params.filename
    res.sendFile(`${__dirname}/uploads/${filename}`)
  })

  server.all('*', (req, res) => {
    return handle(req, res)
  })

  server.listen(3000, err => {
    if (err) throw err
    console.log('> Ready on http://localhost:3000')
  })
})

and in Next.js page do this

import React from 'react';
import axios from 'axios';

class MyPage extends React.Component {
  state = {
    file: '',
    uploadedFile: ''
  }

  handleFormSubmit = (event) => {
    event.preventDefault();
    const file = this.state.file;
    const formData = new FormData();
    formData.append('file', file);
    axios.post('/upload', formData)
      .then(response => {
        const uploadedFile = response.data.file;
        axios.get(`/files/${uploadedFile}`)
          .then(response => {
            const fileURL = response.data;
            this.setState({
              uploadedFile: fileURL
            })
          })
          .catch(error => console.log(error))
      })
      .catch(error => console.log(error))
  }

  render() {
    const { uploadedFile } = this.state;
    return (
      <>
        <h1>File Upload</h1>
        {uploadedFile && (
          <img src={uploadedFile} alt="uploaded-file" />
        )}
        <form onSubmit={this.handleFormSubmit}>
          <input type="file" onChange={(e) => this.setState({ file: e.target.files[0] })}/>
          <button type="submit">Upload</button>
        </form>
      </>
    )
  }
}

export default MyPage;

Note: This is just a basic example to show how you can send a file from Next.js to the server and get it back as a response. modify it as per your needs

you need to know some things when using this approach

  1. handling errors: if there is file with same name in "uploads" folder, the file will be overwritten
  2. this method only handle single file, if you want multiple files you will need to modify the multer configuration for that
Sign up to request clarification or add additional context in comments.

8 Comments

ps. dont forget to install required packages
This doesn't answer the question. It also contains terrible advice: next already has both a static file server and api routes baked in, there is literally no reason to add Express here, not to mention Express hasn't had a release in over a year, which is forever in JS-land. I wouldn't say it's abandoned yet, but I wouldn't use it in a new project either.
@JaredSmith However, the question specifically asked for a way to send a file from outside the public folder, which cannot be achieved using the built-in methods of Next.js. Also, I do not agree that using Express is "terrible" advice. It is a commonly used and reliable library for server-side development in Node.js, and many developers prefer using it for its features and flexibility. Express may not have had a recent release, but it is still actively maintained and supported. It is a personal choice for the developer and there is no right or wrong approach.
My intention was to provide a solution to the specific problem mentioned in the question, As mentioned in the answer, it is not recommended to use Express to serve files in Next.js. But if someone really needs to send files from the server in Next.js, this solution could be an option. There are other ways to achieve this, as you mentioned though I will defer to your expertise in this matter. I am sure there are better ways to handle this in Next.js. Thank you for pointing it out.
@JaredSmith First of all, I want to clarify that I am not arguing with you. I am simply stating the pros and cons ultimately the choice of approach will depend on the specific needs and requirements of the project., It is always important to carefully consider the purpose and strengths of a framework before using it for tasks that may not align with its intended use.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.