如何使用 NextJS 14 使用服务器将文件上传到外部 API

问题描述 投票:0回答:1

[注意]:我需要使用服务器页面,否则我会在 axios 中使用“客户端”组件。

我正在尝试将文件上传到我的外部快递服务器。无论如何,当将其发送到 API 路由时,

req.files.file
为空、未定义或只是空白。

我想提一下,提交表单后触发操作属性时会检测到该文件。

console.log(avatar)
返回以下内容:

file: File {
  size: 476026,
  type: 'image/jpeg',
  name: 'Delta-Force-Operator-in-a-Chinook_4x-scaled.jpg',
  lastModified: 1716527617781
}

无论如何,我面临的麻烦与 Express 服务器有关。我使用一个名为

express-fileupload
的库,为了从即将到来的请求中获取文件,我需要执行类似
req.files.file
的操作,其中
file
是您输入的名称。

exports.uploadObject = asyncHandler(async (req, res, next) => {
  // Prevent execution of code if there is not file in request
  console.dir(req.files);
  
  if (!req.files || Object.keys(req.files).length === 0 || !req.files.file) {
    return next(new ErrorResponse(`No file uploaded`, 400));
  }

  /*
  YADAYADAYADA
  */
      // Return object
      res.status(201).json({
        success: true,
        data: "success"
      });
    });
  });
});

问题

无论我做什么,服务器都不会收到文件,这就是终端上显示的内容

{}
,一个实际的空对象,其中甚至没有字段或值。

这是我在服务器页面中的表单:

const upgradeAvatar = async (formData) => {
  'use server'
  const avatar = formData.get('file')

  const rawFormData = {
    userId: auth?.data?._id,
    username: auth?.data?.username,
    userEmail: auth?.data?.email,
    onModel: 'User',
    album: 'profile-avatars',
    file: avatar,
  }
  // Output
  console.log(rawFormData.file);
  // My function to external API
  await uploadFile(rawFormData)
}

<form action={upgradeAvatar}>
  <label htmlFor="file" className="form-label">
    File
  </label>
  <input
    id="file"
    name="file"
    type="file"
    className="form-control mb-3"
    accept={`image/*`}
  />
  <FormButtons />
</form>

reactjs express next.js fetch-api nextjs14
1个回答
0
投票

最终我无法实现我想做的事情,所以我决定创建一个辅助文件,其中放置所有表单的逻辑和 Api 请求。

'use client'
import { fetchurl, getAuthTokenOnServer } from '@/helpers/setTokenOnServer'
import { useState } from 'react'
import { toast } from 'react-toastify'
import Image from 'next/image'
import UseProgress from '@/components/global/useprogress'
import axios from 'axios'

const Form = ({ auth }) => {
  const [coverData, setCoverData] = useState({
    file: null,
    filename: `Choose file`,
    fileurl: `https://static.vecteezy.com/system/resources/previews/005/337/799/original/icon-image-not-found-free-vector.jpg`,
  })

  const { file, filename, fileurl } = coverData

  const [uploadPercentage, setUploadPercentage] = useState(0)
  const [error, setError] = useState(false)
  const [btnText, setBtnTxt] = useState('Submit')

  const upgradeAvatar = async (e) => {
    e.preventDefault()

    setBtnTxt('Submit...')
    const token = await getAuthTokenOnServer()
    const res = await axios.put(
      `http://localhost:5000/api/v1/uploads/uploadobject`,
      {
        userId: auth?.data?._id,
        username: auth?.data?.username,
        userEmail: auth?.data?.email,
        onModel: 'User',
        file: file,
        album: 'profile-covers',
      },
      {
        headers: {
          'Content-Type': 'multipart/form-data',
          Authorization: `Bearer ${token.value}`,
        },
        onUploadProgress: (ProgressEvent) => {
          setUploadPercentage(
            parseInt(
              Math.round(ProgressEvent.loaded * 100) / ProgressEvent.total
            )
          )
          setTimeout(() => setUploadPercentage(0), 10000)
        },
      }
    )
    await fetchurl(`/auth/updateavatar`, 'PUT', 'no-cache', {
      cover: res.data.data._id,
    })
  }

  const resetForm = () => {
    setCoverData({
      file: null,
      filename: `Choose file`,
      fileurl: `https://static.vecteezy.com/system/resources/previews/005/337/799/original/icon-image-not-found-free-vector.jpg`,
    })
  }

  return (
      <form onSubmit={upgradeAvatar}>
        <label htmlFor="cover" className="form-label">
          File
        </label>
        <input
          id="cover"
          name="file"
          label={file}
          onChange={(e) => {
            const myFile = e.target.files[0]
            let preview = ''
            if (myFile instanceof Blob || myFile instanceof File) {
              preview = URL.createObjectURL(myFile)
            }
            setCoverData({
              file: myFile,
              filename: myFile.name,
              fileurl: preview,
            })
          }}
          type="file"
          className="form-control mb-3"
          placeholder={fileurl}
          accept={`image/*`}
        />
        <UseProgress percentage={uploadPercentage} />
        <button type="submit" className="btn btn-secondary btn-sm float-start">
          {btnText}
        </button>
        <button
          type="button"
          className="btn btn-secondary btn-sm float-end"
          onClick={resetForm}
        >
          Reset
        </button>
      </form>
  )
}

export default Form

然后我只是将 Form 组件调用到我的服务器页面。

import { fetchurl } from '@/helpers/setTokenOnServer'
import Form from './form'

async function getAuthenticatedUser() {
  const res = await fetchurl(`/auth/me`, 'GET', 'no-cache')
  return res
}

const UpdateAvatar = async ({ params, searchParams }) => {
  const auth = await getAuthenticatedUser()

  // Redirect if user is not logged in
  ;(auth?.error?.statusCode === 401 || !auth?.data?.isOnline) &&
    redirect(`/auth/login`)

  return <Form auth={auth} />
}

export default UpdateAvatar
© www.soinside.com 2019 - 2024. All rights reserved.