next.js icon indicating copy to clipboard operation
next.js copied to clipboard

Upload file API stopped working - middleware issue

Open HT-Moh opened this issue 3 years ago • 3 comments

Verify canary release

  • [X] I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
  Platform: linux
  Arch: x64
  Version: #46~20.04.1-Ubuntu SMP Thu Jul 14 15:20:17 UTC 2022
Binaries:
  Node: 16.16.0
  npm: 8.1.2
  Yarn: 1.22.17
  pnpm: N/A
Relevant packages:
  next: 12.2.4
  eslint-config-next: N/A
  react: 18.2.0
  react-dom: 18.2.0

What browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

Describe the Bug

Simply the API to upload the file stopped working. The request hangs and there is no response

image

The request does not reach the breakpoint on the API line 81 if (!request.file) {

when I remove the form-data params the API is called and of course, I get File was not provided

Expected Behavior

It should just work like before

Link to reproduction

/

To Reproduce

To reproduce here the API upload.tsx


/* eslint-disable unicorn/no-null */
import * as mime from 'mime-types'
import multer from 'multer'
import { NextApiRequest, NextApiResponse } from 'next'

export const config = {
  api: {
    bodyParser: false, // Disallow body parsing, consume as stream
  },
}

interface NextApiRequestExtended extends NextApiRequest {
  file: any
  files: any
}

const upload = multer({
  storage: multer.diskStorage({
    destination: (request, file, callback) => {
      const { isProjectPic } = request.query
      const destinationPath =
        isProjectPic === 'true'
          ? `./public/uploads/project`
          : `./public/uploads/profile`
      callback(null, destinationPath)
    },
    filename: (request, file, callback) => {
      const extension = mime.extension(file.mimetype)
      if (typeof extension === `string`) {
        const fileNames = file.originalname.split('.', 1)
        const { userId } = request.query
        if (fileNames.length > 0) {
          const fileName = fileNames[0]
            .toLowerCase()
            .replace(/\s/g, '-')
            // eslint-disable-next-line unicorn/prefer-spread
            .concat('-', userId ? (userId as string) : '', '.', extension)
          callback(null, fileName)
        }
      }
      callback(null, file.originalname)
    },
  }),
  // 1MB
  limits: { fileSize: 1 * 1024 * 1024 },
  fileFilter: (request, file, callback) => {
    if (
      file.mimetype === 'image/png' ||
      file.mimetype === 'image/jpg' ||
      file.mimetype === 'image/jpeg'
    ) {
      callback(null, true)
    } else {
      return callback(new Error('Invalid mime type'))
    }
  },
})

// Helper method to wait for a middleware to execute before continuing
// And to throw an error when an error happens in a middleware
function runMiddleware(
  request: NextApiRequestExtended,
  response: NextApiResponse,
  function_: any,
) {
  return new Promise((resolve, reject) => {
    function_(request, response, (result: any) => {
      if (result instanceof Error) {
        return reject(result)
      }

      return resolve(result)
    })
  })
}
const handler = async (
  request: NextApiRequestExtended,
  response: NextApiResponse,
) => {
  try {
    await runMiddleware(request, response, upload.single('image-upload'))
    if (!request.file) {
      return response.status(404).end('File was not provided')
    }
    const file = request.file
    return response.status(200).send({
      filename: file.filename,
      size: file.size,
      path: file.path,
    })
  } catch (error: any) {
    return response.status(404).end(error.message)
  }
}
export default handler

HT-Moh avatar Aug 07 '22 08:08 HT-Moh

I have middleware.tsx file, when I remove it the upload works. What it's wrong with the middleware implementation?

// middleware.ts
import type { NextRequest } from 'next/server'
import { NextResponse } from 'next/server'
// https://nextjs.org/docs/messages/middleware-upgrade-guide
const PUBLIC_FILE = /\.(.*)$/

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function middleware(request: NextRequest) {
  const shouldHandleLocale =
    !PUBLIC_FILE.test(request.nextUrl.pathname) &&
    !request.nextUrl.pathname.includes('/api/') &&
    request.nextUrl.locale === 'default'

  return shouldHandleLocale
    ? NextResponse.redirect(`/en${request.nextUrl.href}`)
    : undefined
}

HT-Moh avatar Aug 07 '22 10:08 HT-Moh

maybe related to https://github.com/vercel/next.js/issues/39262

HT-Moh avatar Aug 07 '22 12:08 HT-Moh

It happens exactly the same to me!

Pamboli avatar Aug 09 '22 07:08 Pamboli

In my case I have the same issue

arturocasado avatar Sep 14 '22 08:09 arturocasado

I have the same exact issue

rmkasendwa avatar Oct 02 '22 11:10 rmkasendwa

Same here

moatorres avatar Oct 04 '22 15:10 moatorres

I have the same problem with a POST request with a payload of 50Kb

Edit: Fixed skipping api requests in middleware, you can add the following code to middleware.js

export const config = {
    matcher: '/((?!api\/).*)',
}

remorses avatar Oct 05 '22 15:10 remorses

It should be works now.

Check: https://github.com/vercel/next.js/issues/36497#issuecomment-1271419308 🙂

Kikobeats avatar Oct 07 '22 10:10 Kikobeats

This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

github-actions[bot] avatar Nov 06 '22 12:11 github-actions[bot]