JSON.stringify()函数将文件对象替换为一个空对象

时间:2018-11-13 09:37:44

标签: json reactjs spring-boot fetch-api react-bootstrap

问题

大家好,有人可以帮我解决这个复杂的问题吗? : 我正在使用Spring Boot v2.0.5和React.js v15.6.2,ReactDom v15.6.2,React Bootstrap v0.32.4创建一个应用程序,并且前端和服务器端部分之间的链接是通过在Web上使用Spring注释的静态Web服务组成的。后面的API以及前面的API。我的React组件是按照父子设计模式的概念制作的,这意味着:我的某些组件可以是其他组件的子组件,反之亦然。

它如何工作?

我有一个具有列和行的表,表中的每一行都有一个唯一的ID,2个下拉菜单,1个文本输入,2个日期选择器和 1个导致主要问题的文件上传输入;通过单击“ +文档”按钮,用户可以添加更多具有与先前组件相同的行。每行都有一个唯一的编号类型(整数)的增量ID;下拉菜单和输入事件由父组件内部的一种方法根据其标签名称进行处理; 我将用户输入的所有数据存储在对象({})的列表([])中。

示例:如果用户仅填写第一行;存储在列表状态内的对象将是这样的:

[{id:0,type:"forms",lang:"all",libelle:"some strings",dateBegin:"11-12-2018",dateEnd:"12-12-2018",document:{File(154845)}]

如果用户添加了另一行,然后像第一行一样填充了该行,则列表将如下所示:

  [{id:0,type:"forms",lang:"all",libelle:"some strings",dateBegin:"11-12-2018",dateEnd:"12-12-2018",document:{File(154845)},{id:1,type:"howTo",lang:"en",libelle:"some strings",dateBegin:"11-12-2018",dateEnd:"01-01-2019",document:{File(742015)}]

查看此图片以查看表格的外观: Table Demo

该表作为Presentational组件类(主要组件的子代)中的代码

class Presentational extends React.Component {

  constructor(props) {
   super(props);

   this.state = {
    docObjList: [],
    element: (
      <FormDocRowItem // this contains the table tbody tds elements..
        id={1}
        handleChanges={this.props.handleChanges}/>)
   };

     this.handleAddDocumentRow = this.handleAddDocumentRow.bind(this);
 }

  // handleAddDocumentRow method
  handleAddDocumentRow(e) {

   const value = e.target.value;
   const name = e.target.name;

   if (name === 'add') {
    let arr = this.state.docObjList; // get the list state

    // assign the new row component
    arr = [...arr, Object.assign({}, this.state.element)]; 

    // set the new list state
    this.setState({docObjList: arr});
   }

   // if name === 'delete' logic..
 }

   // render method
   render() {
          const {handleReset} = this.props;
      return(
       <FormGroup>
              <Form encType="multipart/form-data">
                <Table striped bordered condensed hover>
                  <thead>
                  <tr>
                    <th>id</th>
                    <th>Type</th>
                    <th>Lang</th>
                    <th>Title</th>
                    <th>Date begin</th>
                    <th>Date end</th>
                    <th>+ Document</th>
                    <th>Options</th>
                  </tr>
                  </thead>
                  <tbody>

                  {this.state.element} // this row is required as initialization
                  {
                    this.state.docObjList.map((doc, index) => {
                      // as index in map() starts from 0 and there is an   
                      // already row component above => The index inside the 
                      // table should start from 1 except The key property 
                      // which should know the right index of the function 
                      const id = index+1; 

                      return (
                        <tr key={index}> 
                          <td>
                            {id}
                          </td>
                          <td>
                            <DocumentTypes id={id} handleChange={this.props.handleChanges}/>
                          </td>
                          <td>
                            <DocumentLanguage id={id} handleChange={this.props.handleChanges}/>
                          </td>
                          <td>
                            <DocumentLibelle id={id} handleChange={this.props.handleChanges}/>
                          </td>
                          <td>
                            <FormControl  id={''+id} name="dateBegin" componentClass="input" type="date"
                                         onChange={this.props.handleChanges}/>
                          </td>
                          <td>
                            <FormControl  id={''+id} name="dateEnd" componentClass="input" type="date"
                                         onChange={this.props.handleChanges}/>
                          </td>
                          <td>
                            <Document  id={id} handleChange={this.props.handleChanges}/>
                          </td>
                          {
                            this.state.docObjList.length == index + 1 &&
                            <td>

                              <button type="button" style={{verticalAlign: 'middle', textAlign: 'center'}} id={index + 1}
                                      name="delete"
                                      onClick={this.handleAddDocumentRow}>
                                Delete
                              </button>
                            </td>
                          }
                        </tr>
                      );
                    })
                  }
                  </tbody>
                </Table>
                <button type="button" name="add" onClick={this.handleAddDocumentRow}>+ Document</button>

                <FormGroup>
                  <Button type="reset"
                          style={{marginRight: '20%'}}
                          className="btn-primary"
                          onClick={this.props.handleClickSubmit}>Submit</Button>
                  <Button name="back" onClick={this.props.handleClickSubmit}>Annuler</Button>
                </FormGroup>
              </Form>
       </FormGroup>
        )
  }
}

行组件类(Presentational的子组件)

const FormDocRowItem = (props) => {

  const {id} = props; // the ID here is refering the column that is going to be 
                      // show inside the table not the index of the map function
  return(
    return (
      <tr>
        <td>
          {id}
        </td>
        <td>
          <DocumentTypes id={id} handleChange={this.props.handleChanges}/>
        </td>
        <td>
          <DocumentLanguage id={id} handleChange={this.props.handleChanges}/>
        </td>
        <td>
          <DocumentLibelle id={id} handleChange={this.props.handleChanges}/>
        </td>
        <td>
          <FormControl id={''+id} name="dateBegin" componentClass="input" type="date" onChange={this.props.handleChanges}/>
        </td>
        <td>
          <FormControl id={''+id} name="dateEnd" componentClass="input" type="date" onChange={this.props.handleChanges}/>
        </td>
        <td>
          <Document id={id} handleChange={this.props.handleChanges}/>
        </td>
      </tr>
    );
  }

}

父组件类(主要组件)

   constructor(props) {
     this.state ={
       docDataList: [],
       formIsReadyToSubmit: false
     } 

     this.handleSubmit = this.handleSubmit.bind(this); // button submit click
     this.handleReset = this.handleReset.bind(this); // button reset click
     this.fillWithData = this.fillWithData.bind(this); // handle changes

   }

   // handleReset method..

   fillWithData(e) {

    const name = e.target.name; // get the name of the target
    const id = parseInt(e.target.id); // get the id of the target
    let value = e.target.value; // get the value of the target
    let arr = this.state.docDataList; // get the list state

    // if the target is a file upload
    if (name === 'selectDocument') 
      value = e.target.files[0];

    // create properties with null values starting from the first onchange 
    // event handling, to not get a misplaced properties inside the
   // objects of the list state
    arr.map((x) => {
      x.type = x.type ? x.type : null;
      x.lang = x.lang ? x.lang : null;
      x.libelle = x.libelle ? x.libelle : null;
      x.dateBegin = x.dateBegin ? x.dateBegin : null;
      x.dateEnd = x.dateEnd ? x.dateEnd : null;
      x.document = x.document ? x.document : null;
    });

    // if the event target name is not delete
    if (name != 'delete') {
      // check if the object id already exist in the table
      // if it exists, the new value should replace the previous one
      // and not allowed to add a new object to the list state
      if ((arr.find((x) => x.id == id))) {
        // loop through the list state to find the id of the object
        arr.map((x) => {
          if (x.id == id) {
            // helper variable to prevent empty strings
            const val = value != '' ? value : null;

            switch (name) {
              case 'selectType':
                x.type = val;
                break;
              case 'selectLang':
                x.lang = val;
                break;
              case 'libelle':
                x.libelle = val;
                break;
              case 'dateBegin':
                x.dateBegin = val;
                break;
              case 'dateEnd':
                x.dateEnd = val;
                break;
              case 'selectDocument':
                x.document = val;
                break;
            }
          }
        });
        // assign the new list to my docDataList state
        // mentioning that the id of the element already exist
        this.setState({docDataList: arr}, () => {
          console.log(' ID exist; new dataList :', this.state.docDataList);
        });
      }
      // if the id doesn't exist (means that the button +document is clicked)
      else {
        // again, a helper variable as the previous statement 
        const val = value != '' ? value : null;

        this.setState({
          docDataList: [...arr, Object.assign({
            id: id,
            type: name === 'selectType' ? val : null,
            lang: name === 'selectLang' ? val : null,
            libelle: name === 'libelle' ? val : null,
            dateBegin: name === 'dataBegin' ? val : null,
            dateEnd: name === 'dateEnd' ? val : null//,
            //document: name==='selectDocument'? val:null
          })]
        }, () => {
          console.log('ID doesnt exist; new dataList :', this.state.docDataList);
        });
      }
    }
  }

HandleSubmit()方法(在Parent组件类内部)

// Submit button click handler
  handleSubmit(e) {

      let docDataList = this.state.docDataList;

      // if the user didn't touch any thing on the table rows
      // that means the list is empty and its length = 0
      if (docDataList.length === 0) {
        this.setState({
          alerts: {
            message: 'Please enter your document information ',
            show: true
          }
        });
      }
      // if the user has entered a data on the table row
      else if (docDataList.length > 0) {

        let data = new FormData(); // object which will be sent 

        // check the docDataList before request
        console.log('DocDataList before request:', docDataList); 
        data.append('docDataList', JSON.stringify(docDataList)); 

        fetch('http://localhost:8080/api/files/uploadFile', {
          method: 'POST',
          body: data
        }).then(response => {
          console.log('success document upload', response);
        }).catch(error => {
          console.log('error', error);
        });

        this.setState({
          formIsReadyToSubmit: true, 
          docDataList: [], // reset the list
          alerts: {updateAlert: true} // make an alert
        });
      }

    }

要查看当我用数据填充行时控制台显示的内容:CLICK HERE PLEASE

要查看请求的响应:CLICK HERE PLEASE

注意:在观看了这些屏幕截图后,您可能会注意到,还有一个名为“ arrContrats”的数据列表,我在本期中没有提及,因为它没有任何问题;问题出在“ docDataList”列表上。预先感谢

2 个答案:

答案 0 :(得分:1)

如果您的问题是从浏览器获取File对象,然后在其上使用JSON.stringify(或包含它的东西)并在其中获取{} JSON,这是正确的。浏览器的File对象没有自己的可枚举属性。 JSON.stringify仅包含自己的可枚举属性。

如果您希望File对象具有各种属性(继承的访问器属性),则需要将它们复制到新对象中。

如果要获取文件数据,则无法直接从File对象获取它,必须为此使用FileReader

答案 1 :(得分:0)

function stringifyFileObject(arrWithFiles = []) {
  const arrData = [];
   for (let i=0; i < arrWithFiles.length; i++) {
    const file = arrWithFiles[i];
    const obj = {
        lastModified: file.lastModified,
        name: file.name,
        size: file.size,
        type: file.type,
        webkitRelativePath: file.webkitRelativePath,
    }
    arrData.push( obj );
  }
}

或者任何适合您的需求。你明白了...