无法在remix/node.js上通过axios上传文件到django

sqxo8psd  于 12个月前  发布在  iOS
关注(0)|答案(1)|浏览(132)

我们有一个remix应用程序,它需要上传一个文件并将其转发到django后端(django rest框架)。然而,django后端没有收到请求中的任何数据。
在remix后端,我们有一个动作来上传文件:

export const action = async ({ request }: ActionFunctionArgs) => {
  const user = await auth.isAuthenticated(request, {
    failureRedirect: paths.login,
  });

  const uploadHandler = unstable_createMemoryUploadHandler({
    maxPartSize: 500_000,
  });

  const formData = await unstable_parseMultipartFormData(
    request,
    uploadHandler,
  );

  const response = await axios.post("/assets/upload/", formData, {
    headers: {
      "Content-Type": "multipart/form-data",
    },
  });
};

字符串
在这个动作中,formData看起来很好。它有一个条目[“myFile”,]。看起来Content-Length头并没有自动生成。Django接收到Content-Length的值为0。但是,边界会自动添加到Content-Type头。
我们尝试了很多方法来解决这个问题。例如,从FormData对象中阅读文件到Blob中,并使用它创建一个新的FormData对象:

const fileToBlob = async (file: File) =>
    new Blob([new Uint8Array(await file.arrayBuffer())], { type: file.type });

  const formDataNew = new FormData();
  formDataNew.append(
    "myFile",
    await fileToBlob(formData.get("myFile") as File),
  );


或者使用form-data库创建一个新的FormData对象,并使用formDataFromLib.getHeaders()"Content-Length": formDataFromLib.getLengthSync()手动计算和设置content-length header。或者根本不设置header(让axios处理事情- axios然后将content-type设置为application/json).都不起作用。
对此有什么想法吗?

tct7dpnv

tct7dpnv1#

由于Remix使用fetch API,因此您可以将正文直接流式传输到后端,而无需中间处理。

// routes/upload.tsx
import {
  type ActionFunctionArgs,
  json,
} from "@remix-run/node";

import { Form, useActionData } from "@remix-run/react";

export const action = async ({ request }: ActionFunctionArgs) => {
  // stream the request body directly to the backend endpoint
  const response = await fetch("http://localhost:3000/upload2", {
    method: "POST",
    headers: {
      // get the content type from the original request
      // since it also includes the boundary needed by multipart/form-data
      "Content-Type": request.headers.get("Content-Type")!,
    },
    // just pass the request body through
    body: request.body,
  });

  const result = await response.json();
  return json(result);
};

export default function Component() {
  const actionData = useActionData<typeof action>();

  return (
    <div>
      <Form method="post" encType="multipart/form-data">
        <input type="file" name="file" />
        <button type="submit">Submit</button>
      </Form>
      <pre>{JSON.stringify(actionData, null, 2)}</pre>
    </div>
  );
}

字符串
为了验证,这里是“后端”端点。它将保存到./uploads目录。

// routes/upload2.tsx
import {
  type ActionFunctionArgs,
  json,
  unstable_createFileUploadHandler,
  unstable_parseMultipartFormData,
} from "@remix-run/node";

export const action = async ({ request }: ActionFunctionArgs) => {
  const uploadHandler = unstable_createFileUploadHandler({
    directory: "./uploads",
    maxPartSize: 500_000,
  });

  const formData = await unstable_parseMultipartFormData(
    request,
    uploadHandler
  );
  const file = formData.get("file") as File;

  return json({ success: true, filename: file.name });
};

相关问题