ReactJS:如何使用 Formik 处理图像/文件上传?

Posted

技术标签:

【中文标题】ReactJS:如何使用 Formik 处理图像/文件上传?【英文标题】:ReactJS: How to handle Image / File upload with Formik? 【发布时间】:2019-10-02 14:56:03 【问题描述】:

我正在使用ReactJS 为我的网站设计个人资料页面。 现在我的问题是如何从本地机器上传图像并将其保存到数据库并在个人资料页面中显示它

import React, Component from 'react';
import  connect  from 'react-redux';
import  AccountAction  from '../../actions/user/AccountPg1Action';
import  Formik, Form, Field, ErrorMessage  from 'formik';
import * as Yup from 'yup';

class AccountInfo extends Component 
  constructor(props) 
    super(props) 
    this.state = 
      currentStep: 1,
      userAccountData: 
        userid: '',
        useravtar: '',
        attachement_id: '',
   
  
 

handleFileUpload = (event) => 
  this.setState(useravtar: event.currentTarget.files[0])
;

handleChange = event => 
    const name, value = event.target
    this.setState(
      [name]: value
    )    
  

handleSubmit = event => 
    let that = this;
    const  AccountAction  = that.props;
    event.preventDefault();

    let accountInputs = 
      userid: 49,
      useravtar: that.state.image,
      attachement_id: 478,

    that.setState(
      userAccountData: accountInputs,
    )

    AccountAction(accountInputs)
  
AccountInfoView = () => 
console.log(this.state.useravtar)
    return (
      <section id="account_sec" className="second_form">
      <div className="container">
      <React.Fragment>
        <Formik
          initialValues=‌
            file: null,
            email: '',
            phone: ''
          
          validationSchema=accountInfoSchema
          render=(values) => 
          return(
        <Form onSubmit=this.handleSubmit>
        <Step1 
          currentStep=this.state.currentStep 
          handleChange=this.handleChange
          file= this.state.useravtar
          handleFileUpload=this.handleFileUpload
          />
          </Form>
        );
      
      />
      </React.Fragment>
      )
  

  render()     

    return (
      <div>this.authView()</div>
    )
  


function Step1(props) 
console.log(props.useravtar)
  if (props.currentStep !== 1) 
    return null
   

  return(
    <div className="upload">
        <label htmlFor="profile">
          <div className="imgbox">
            <img src="images/trans_116X116.png"  />
            <img src=props.useravtar className="absoImg"  />
          </div>
        </label>
<input id="file" name="file" type="file" accept="image/*" onChange=props.handleFileUpload/>
        <span className="guide_leb">Add your avatar</span>
      </div>
  )

当我在 handleChange 中为 event.target.file[0] 操作控制台时,它会以未定义的方式响应。

另外,在handleSubmit 操作中执行console.log(this.state.useravtar) 会显示类似c:/fakepath/imgname.jpg 的路径名

P.S:我有多种形式,所以我在 Step 明智地使用它。我正在使用 Redux Reducer 来存储数据。

我已经提到了this 链接,但我的要求不是这样。

【问题讨论】:

您是否尝试过使用表单数据对象 - developer.mozilla.org/en-US/docs/Web/API/FormData/… @SumanthMadishetty :我使用了 Formik 库,所以我使用了该库中的 Field 组件。 jaredpalmer.com/formik @Taalavya formik 没有文件上传组件,你必须使用html输入并使用formik的`setFieldValue`方法来设置数据 【参考方案1】:

Formik 默认不支持文件上传,但是你可以试试下面的

<input id="file" name="file" type="file" onChange=(event) => 
  setFieldValue("file", event.currentTarget.files[0]);
 />

这里"file" 代表您用于保存文件的密钥

在提交时,您可以使用

获取文件的文件名、大小等
onSubmit=(values) => 
        console.log( 
              fileName: values.file.name, 
              type: values.file.type,
              size: `$values.file.size bytes`
            )

如果要将文件设置为组件状态,则可以使用

onChange=(event) => 
  this.setState("file": event.currentTarget.files[0]);

根据您的代码,您必须按以下方式处理文件上传

在 AccountInfo 中添加处理文件上传的函数

handleFileUpload = (event) => 
this.setState(WAHTEVETKEYYOUNEED: event.currentTarget.files[0]);

并将相同的功能传递给 Step1 组件,如下所示

    <Step1 
      currentStep=this.state.currentStep 
      handleChange=this.handleChange
      file= this.state.image
      handleFileUpload=this.handleFileUpload
      />

在上传文件的 Step1 组件中,将输入更改为

<input id="file" name="file" type="file" accept="image/*" onChange=props.handleFileUpload/>

如果您需要预览上传的图像,则可以创建一个 blob 并传递与图像源相同的内容,如下所示

<img src=URL.createObjectURL(FILE_OBJECT) /> 

EDIT-1

由于URL.createObjectURL方法由于安全问题已被弃用,我们需要将srcObject用于Media Elements,要使用可以使用ref来分配srcObject,例如

假设您使用的是类组件,

构造函数

在构造函数中可以使用

constructor(props) 
  super(props)
  this.imageElRef = React.createRef(null)

处理更改功能

handleFileUpload = (event) => 
  let reader = new FileReader();
let file = event.target.files[0];
reader.onloadend = () => 
  this.setState(
    file: reader.result
  );
;
reader.readAsDataURL(file);

元素

<img src=this.state.file /> 

【讨论】:

在哪里定义这个setFieldValue,它会给我一个未定义的错误,例如:./src/components/user/AccountInfo.jsx Line 266: 'setFieldValue' is not defined no-undef setFieldValue是从&lt;Formik /&gt;获取的,参考:jaredpalmer.com/formik/docs/api/… @Taalavya 如果您想直接存储文件以声明您可以使用我提到的方法 @SumanthMadishetty 是的,但它从 function Step1(props)... 函数中抛出此错误。 prnt.sc/np7gnp 你用props.setState是什么意思...你能用实现更新代码吗【参考方案2】:

您可以使用 Formik 上传单个或多个文件并进行验证,如下所示:

import "./App.css";
import  useEffect, useState  from "react";
import * as Yup from "yup";
import  Formik, Field, Form, ErrorMessage, useField  from "formik";
import axios from "axios";


function App() 
  return (
    <Formik
      initialValues=
        profile: [],
      
      validationSchema=Yup.object(
        profile:Yup.array().min(1,"select at least 1 file")
      )
      onSubmit=(values, props) => 
        let data = new FormData();
        values.profile.forEach((photo, index) => 
          data.append(`photo$index`, values.profile[index]);
        );
        axios
          .post("you_api_for_file_upload", data, 
            headers: 
              "Content-Type": "multipart/form-data",
            ,
          )
          .then((response) => 
            console.log(response);
          )
          .catch((err) => 
            console.log(err);
          );
      
    >
      (formik) => 
        return (
          <>
            <Form>
              <input
                id="file"
                name="profile"
                type="file"
                onChange=(event) => 
                  const files = event.target.files;
                  let myFiles =Array.from(files);
                  formik.setFieldValue("profile", myFiles);
                
                multiple
              />
              <ErrorMessage name="profile"/>
              <button type="submit" disabled=formik.isSubmitting>
                Submit
              </button>
            </Form>
          </>
        );
      
    </Formik>
  );


export default App;

注意:您可以根据需要自定义 min(your choice, "your message")

   validationSchema=Yup.object(
        profile:Yup.array().min(1,"select at least 1 file")
      )

【讨论】:

输入名称和setFieldValue不能作为同一个值,只有字符串类型才可以使用。对于字符串以外的类型,setFieldValue 必须是不同的键【参考方案3】:

这是我使用 FormikMaterial UI

解决的方法

在您的 JS 文件中,只需声明一个变量 avatarPreview 如下所示

  const [avatarPreview, setAvatarPreview] = useState('/avatars/default.png');



           <Box
            display='flex'
            textAlign='center'
            justifyContent='center'
            flexDirection='column'>
           
            <ImageAvatar size='md' src=avatarPreview || user?.avatar />

            <Button
              variant='contained'
              component='label'
              startIcon=<CloudUploadIcon />>
              Choose Avatar
              <input
                name='avatar'
                accept='image/*'
                id='contained-button-file'
                type='file'
                hidden
                onChange=(e) => 
                  const fileReader = new FileReader();
                  fileReader.onload = () => 
                    if (fileReader.readyState === 2) 
                      setFieldValue('avatar', fileReader.result);
                      setAvatarPreview(fileReader.result);
                    
                  ;
                  fileReader.readAsDataURL(e.target.files[0]);
                
              />
            </Button>
          </Box>

默认预览:

选择头像后:

【讨论】:

【参考方案4】:

要使用后端处理 formik 中的文件,您只需添加下面给出的输入(您可以将 avatar 更改为您想要的任何内容):

<input
 type="file"
 name="avatar"
 onChange=(event) => 
 setFieldValue('avatar', event.currentTarget.files[0]);
 
 />

onSubmit 您可以使用值 obj 访问文件,例如 values.avatar

在服务器端(快速)访问我们使用的文件 req.file 您还必须使用 multer 和 cloudinary 来检测文件以在服务器端处理它

【讨论】:

以上是关于ReactJS:如何使用 Formik 处理图像/文件上传?的主要内容,如果未能解决你的问题,请参考以下文章

reactjs - 如何在formik中将状态值传递给初始值

如何将 react-intl-tel-input 与 formik 集成?

reactjs:使用 reactstrap ui 的带有数据时间选择器的 formik 表单示例

如何使用formik react js在handlesubmit函数中接收选择值?

React JS:Yup & Formik 错误消息在提交时显示多次

在 formik 组件之外处理 formik 表单