ruby-on-rails 上传多个文件- Rails和React

j1dl9f46  于 2023-05-02  发布在  Ruby
关注(0)|答案(1)|浏览(137)

我正在尝试上传多个文件,这是导师为学生上传的作业的一部分。然后学生就可以点击链接并将文件下载到他们的计算机上。
下面是上传文件时的表单:

import React, { useState, useContext } from "react";
import { UserContext } from "./App";
import { useLocation, useNavigate } from "react-router-dom";
import { Container, Form, Row, Col, Button } from "react-bootstrap";

function AssignWork() {
  const [error, setError] = useState([]);
  const location = useLocation();
  const navigate = useNavigate();
  const { studentId } = location.state;
  const { currentUser, setStudents } = useContext(UserContext);
  const [assignmentData, setAssignmentData] = useState({
    name: "",
    notes: "",
    subject: "",
    tutor_id: currentUser.id,
    student_id: studentId,
    files: "",
  });
  // const [files, setFiles] = useState(null);

  function handleChange(e) {
    setAssignmentData({ ...assignmentData, [e.target.name]: e.target.value });
  }

  function onFormSubmit(e) {
    e.preventDefault();

    // const formData = new FormData();

    // for (let data in assignmentData) {
    //   formData.append(data, assignmentData[data]);
    // }

    fetch("/assignments", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(assignmentData),
    }).then((resp) => {
      if (resp.ok) {
        resp.json().then((newAssignment) => {
          console.log(newAssignment);
          let studentsList = [
            ...new Map(
              newAssignment.tutor.students.map((student) => [
                student["id"],
                student,
              ])
            ).values(),
          ];
          setStudents(studentsList);
          navigate("/students");
        });
      } else {
        resp.json().then((error) => setError(error.errors));
      }
    });
  }

  return (
    <Container
      style={{
        marginTop: "24px",
        padding: "24px",
        border: ".5px solid grey",
        borderRadius: "8px",
        width: "75%",
      }}
    >
      <h2
        style={{
          border: ".5px solid grey",
          marginBottom: "24px",
          borderRadius: "8px",
          padding: "8px",
        }}
      >
        Assignment
      </h2>
      <Form onSubmit={onFormSubmit}>
        <Row>
          <Col>
            <Form.Label>Name:</Form.Label>
            <Form.Control
              type='text'
              name='name'
              value={assignmentData.name}
              onChange={handleChange}
            />
          </Col>
          <Col>
            <Form.Label>Subject:</Form.Label>
            <Form.Control
              type='text'
              name='subject'
              value={assignmentData.subject}
              onChange={handleChange}
            />
          </Col>
        </Row>
        <Row style={{ marginTop: "16px" }}>
          <Col>
            <Form.Label>Notes:</Form.Label>
            <Form.Control
              as='textarea'
              name='notes'
              value={assignmentData.notes}
              onChange={handleChange}
            />
          </Col>
        </Row>
        <Row style={{ marginTop: "16px" }}>
          <Col>
            <Form.Control
              type='file'
              name='file'
              multiple
              onChange={(e) =>
                setAssignmentData({ ...assignmentData, files: e.target.files })
              }
            />
          </Col>
        </Row>
        <Row
          style={{
            margin: "16px",
            justifyContent: "center",
          }}
        >
          <Button type='submit' variant='success' style={{ maxWidth: "33%" }}>
            Upload
          </Button>
        </Row>
        {/* {error.map((error) => {
          return <p key={error}>{error}</p>;
        })} */}
      </Form>
    </Container>
  );
}

export default AssignWork;

我无法将文件正确发送到Rails后端。它要么在命中'byebug'之前抛出一个错误,要么命中byebug但params[:files]为nil。我还需要一种方法来呈现文件的URL时,将其发送回前端。文件的存储目前是通过ActiveStorage完成的,ActiveStorage为用户存储头像。
这是当前的后端代码,当我试图解决这个问题时,大部分都被注解掉了:

def create
  byebug
  # assignment = Assignment.create!(new_assignment_params)
  # assignment.update({ files: params[:files] })
  # render json: assignment, include: "tutor.students.assignments", status: :created
end

def destroy
  assignment_to_delete = find_assignment
  if assignment_to_delete.tutor_id == @current_user.id
    assignment_to_delete.destroy
    head :no_content
  else
    render json: { error: "You do not have permission to destroy this assignment." }, status: :unauthorized
  end
end

private

def new_assignment_params
  params.permit(:name, :subject, :notes, :tutor_id, :student_id, :files)
end

链接到repo:https://github.com/mykovasyl/tutor-plus.我正在运行Rails 6!
任何帮助是赞赏!!

oyjwcjzk

oyjwcjzk1#

您正在尝试在JSON请求中发送文件。那是行不通的您需要多部分表单请求来发送二进制格式的文件。要做到这一点,你需要post ing一个FormData对象,它将使用multipart/form-data内容类型(当使用FormData对象和fetch时,这是自动设置的。您还可以通过将json作为FormData上的属性来发送一些JSON文件。

function onFormSubmit(e) {
  e.preventDefault()

  const { files, ...otherAssignmentData } = assignmentData
  const formData = new FormData()

  formData.append(
    "data",
    new Blob(
      [
        JSON.stringify(otherAssignmentData),
      ],
      {
        type: "application/json",
      }
    )
  )

  for (let file in files) {
    formData.append(file.name, file)
  }

  fetch("/assignments", {
    method: "POST",
    body: formData,
  }).then((resp) => {
    if (resp.ok) {
      resp.json().then((newAssignment) => {
        console.log(newAssignment)
        let studentsList = [
          ...new Map(
            newAssignment.tutor.students.map((student) => [
              student["id"],
              student,
            ])
          ).values(),
        ]
        setStudents(studentsList)
        navigate("/students")
      })
    } else {
      resp.json().then((error) => setError(error.errors))
    }
  })
}

现在发送到服务器的不是JSON,而是多部分表单数据,看起来像这样。
提交的请求主体中的“部分”与文件一样多,另外还有一个JSON元数据的“data”键。
在服务器上还有更多的工作要做。您的服务器将需要解析多部分表单数据,并分别在data键上提取文件(我已经根据文件名键入了这些文件)和JSON。我不是很熟悉ruby on rails,但我认为它可以开箱即用。检查传入请求的参数。

相关问题