【问题标题】:converting from blob to binary to save it to mongodb从 blob 转换为二进制以将其保存到 mongodb
【发布时间】:2018-04-11 05:25:54
【问题描述】:

我从前端获取这样的对象到我的节点快递服务器。

{ picture: [ { preview: 'blob:http://localhost:3000/1f413443-83d8-499e-a432-9ac51a2592b7' } ],
  name: 'fsdfs',
  description: 'fdfd',
  url: 'fdfd',
  about: 'dfdf' }

我得到这个错误:

TypeError: path must be a string or Buffer
    at TypeError (native)

这是我保存到 mongodb 的函数

exports.create_a_project = function(req, res) {
  console.log(req.body);
  var new_project = new Project(req.body);

  new_project.picture.data = fs.readFileSync(req.body.picture[0]);
  new_project.picture.contentType = 'image/png';
  new_project.save(function(err, project) {
    if (err)
      res.send(err);
    res.json(project);
  });
};

我需要如何将接收到的图像转换为二进制以便保存。 或者我需要从客户端将其作为二进制 base64 发送。

我的客户端我使用 react redux Dropzone 来发送我的数据。

这是我的表格及其外观。

import React from 'react';
import {Field, reduxForm} from 'redux-form';
import Dropzone from 'react-dropzone';

const FILE_FIELD_NAME = 'picture';

const renderDropzoneInput = (field) => {
  const files = field.input.value;
  return (
      <div>
        <Dropzone
            name={field.name}
            onDrop={(filesToUpload, e) => field.input.onChange(filesToUpload)}
        >
          <div>Try dropping some files here, or click to select files to
            upload.
          </div>
        </Dropzone>
        {field.meta.touched &&
        field.meta.error &&
        <span className="error">{field.meta.error}</span>}
        {files && Array.isArray(files) && (
            <ul>
              {files.map((file, i) => <li key={i}>{file.name}</li>)}
            </ul>
        )}
      </div>
  );
};

const validate = values => {
  const errors = {};
  if (!values.name) {
    errors.name = 'Required';
  } else if (values.name.length > 15) {
    errors.name = 'Must be 15 characters or less';
  }
  if (!values.description) {
    errors.description = 'Required';
  } else if (values.description.length > 15) {
    errors.description = 'Must be 75 characters or less';
  }

  if (!values.url) {
    errors.url = 'Required';
  } else if (values.url.length > 15) {
    errors.url = 'Must be 15 characters or less';
  }
  if (!values.about) {
    errors.about = 'Required';
  } else if (values.about.length > 15) {
    errors.about = 'Must be 15 characters or less';
  }
  if (!values.picture) {
    errors.picture = 'Required';
  } else if (values.picture.length > 15) {
    errors.picture = 'Must be 15 characters or less';
  }
  // if (!values.email) {
  //   errors.email = 'Required';
  // } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
  //   errors.email = 'Invalid email address';
  // }
  // if (!values.age) {
  //   errors.age = 'Required';
  // } else if (isNaN(Number(values.age))) {
  //   errors.age = 'Must be a number';
  // } else if (Number(values.age) < 18) {
  //   errors.age = 'Sorry, you must be at least 18 years old';
  // }
  return errors;
};

const warn = values => {
  const warnings = {};
  // if (values.age < 19) {
  //   warnings.age = 'Hmm, you seem a bit young...';
  // }
  return warnings;
};

const renderField = ({input, label, type, meta: {touched, error, warning}}) => (
    <div>
      <label>{label}</label>
      <div>
        <input {...input} placeholder={label} type={type}/>
        {touched && ((error && <span>{error}</span>) ||
            (warning && <span>{warning}</span>))}
      </div>
    </div>
);

const SyncValidationForm = (props) => {
  const {handleSubmit, pristine, reset, submitting} = props;
  return (
      <form onSubmit={handleSubmit}>
        <Field name="name" type="text" component={renderField}
               label="Name"/>
        <Field name="description" type="text" component={renderField}
               label="Description"/>
        <Field name="url" type="text" component={renderField}
               label="Url"/>
        <Field name="about" type="text" component={renderField}
               label="About"/>
        {/*<Field name="picture" type="text" component={renderField}*/}
               {/*label="Picture"/>*/}
        <Field
            name={FILE_FIELD_NAME}
            component={renderDropzoneInput}
        />
        {/*<Field name="email" type="email" component={renderField} label="Email"/>*/}
        {/*<Field name="age" type="number" component={renderField} label="Age"/>*/}
        <div>
          <button type="submit" disabled={submitting}>Submit</button>
          <button type="button" disabled={pristine || submitting}
                  onClick={reset}>Clear Values
          </button>
        </div>
      </form>
  );
};

export default reduxForm({
  form: 'syncValidation',  // a unique identifier for this form
  validate,                // <--- validation function given to redux-form
  warn                     // <--- warning function given to redux-form
})(SyncValidationForm);

这是我处理其余 api 的服务函数。

const addProject = (newProject) => {

  let data = JSON.stringify(newProject);



  return axios.post('http://localhost:3008/projects', data, {
        headers: {
          'Content-Type': 'application/json',
        }
      }
  ).then(response => {
    // console.log(response)
  }).catch(error => {
    console.log(error)
  });
};

【问题讨论】:

    标签: mongodb reactjs react-redux dropzone.js


    【解决方案1】:

    所以我认为你的问题出在这一行:

    new_project.picture.data = fs.readFileSync(req.body.picture[0]);
    

    您插入 data 的 mongoDB 表列给了您该错误。它需要一个字符串或缓冲区,而你给了它一个 File 对象。

    您可以使用 what I posted here 获取 base64 字节字符串,我将尝试将其与您的代码集成,如下所示:

    您需要确保有一个变量来收集文件。这就是我设置页面顶部的方式:

    import React from 'react'
    import Reflux from 'reflux'
    import {Form, Card, CardBlock, CardHeader, CardText, Col, Row, Button } from 'reactstrap'
    import actions from '~/actions/actions'
    import DropZone from 'react-dropzone'
    
    // stores
    import SomeStore from '~/stores/someStore.jsx'
    
    Reflux.defineReact(React)
    export default class myUploaderClass extends Reflux.Component {
      constructor(props) {
        super(props);
        this.state = {
            attachments: [],
        };
        this.stores = [
            SomeStore,
        ]
        ....
    

    然后绑定新函数:

        ....
        this.getData = this.getData.bind(this);
        this.processFile = this.processFile.bind(this);
        this.errorHandler = this.errorHandler.bind(this);
        this.progressHandler = this.progressHandler.bind(this);
      } // close constructor
    

    然后我们将字节提供给attachments 并将其发送给new_project.picture.data。对我来说,我使用了一个使用onDrop={this.uploadFile} 运行DropZone 的onDrop 的函数。我真的不知道你在做什么,因为我不知道filesToUpload 指的是什么。我的uploadFile 看起来像这样:

    uploadFile(event){
      this.setState({
        files: event,
      });
    
      document.getElementById('docDZ').classList.remove('dragover');
      document.getElementById('progress').textContent = '000';
      document.getElementById('progressBar').style.width = '0%';
    
      this.state.files = event;  // just for good measure
      for (let i = 0; i < this.state.files.length; i++) {
        const a = i + 1;
        console.log('in loop, pass: ' + a);
        const f = this.state.files[i];
    
        this.getData(f); // this will load the file to SomeStore.state.attachments
      }
    }
    

    这将是为每个拖放/添加到 DropZone 的文件运行的 getData 函数:

    getData(f) {
        const reader = new FileReader();
        reader.onerror = this.errorHandler;
        reader.onprogress = this.progressHandler;
        reader.onload = this.processFile(f);
        reader.readAsDataURL(f);
    }
    

    然后从onload 运行processFile()

    processFile(theFile) {
      return function(e) {
        const bytes = e.target.result.split('base64,')[1];
        const fileArray = [];
    
        // *** Collect any existing attachments ***
        // I found I could not get it from this.state, but had to use
        // my store, instead
        if (SomeStore.state.attachments.length > 0) {
          for (let i=0; i < SomeStore.state.attachments.length; i++) {
            fileArray.push(SomeStore.state.attachments[i]);
         }
        }
    
        // Add the new one to this.state
        fileArray.push(bytes);
    
        // Update the state
        SomeStore.setState({
          attachments: fileArray,
        });
        // This seemed to automatically add it to this.state, for me.
      }
    }
    

    一旦你有了它,你应该能够做到:

    new_project.picture.data = this.state.attachments[0];
    

    如果没有,出于某种原因,您可能会尝试在 exports.create_a_project() 中调用它,作为您做的第一件事:

    getData(req.body.picture[0]);
    

    这甚至可以工作,而无需修改您现有的 onDrop 例程。如果this.state.attachments 没有任何内容,那么您的SomeStore.state.attachments 肯定应该有,假设您已将其保存到名为stores 的文件夹中为someStore.jsx

    import Reflux from 'reflux'
    import Actions from '~/actions/actions`
    
    class SomeStore extends Reflux.Store
    {
        constructor()
        {
            super();
            this.state = {
                attachments: [],
            };
            this.listenables = Actions;
            this.baseState = {
                attachments: [],
            };
        }
    
        onUpdateFields(name, value) {
            this.setState({
                [name]: value,
            });
        }
    
        onResetFields() {
            this.setState({
               attachments: [],
            });
        }
    }
    const reqformdata = new SomeStore
    
    export default reqformdata
    

    附加功能

    errorHandler(e){
        switch (e.target.error.code) {
          case e.target.error.NOT_FOUND_ERR:
            alert('File not found.');
            break;
          case e.target.error.NOT_READABLE_ERR:
            alert('File is not readable.');
            break;
          case e.target.error.ABORT_ERR:
            break;    // no operation
          default:
            alert('An error occurred reading this file.');
            break;
        }
      }
    
    progressHandler(e) {
        if (e.lengthComputable){
          const loaded = Math.round((e.loaded / e.total) * 100);
          let zeros = '';
    
          // Percent loaded in string
          if (loaded >= 0 && loaded < 10) {
            zeros = '00';
          }
          else if (loaded < 100) {
            zeros = '0';
          }
    
          // Display progress in 3-digits and increase bar length
          document.getElementById("progress").textContent = zeros + loaded.toString();
          document.getElementById("progressBar").style.width = loaded + '%';
        }
      }
    

    还有我的 DropZone 和适用的进度指示器标记​​:

    render(){
    
    const dropZoneStyle = {
      height: "34px",
      width: "300px",
      border: "1px solid #ccc",
      borderRadius: "4px",
    };
    
    return (
      <Form>
        <Col xs={5}>
                <DropZone type="file" id="docDZ"
                  onDrop={this.uploadFile}
                  onDropRejected={this.onDropRejected}
                  onClick={this.handleUploadClick}
                  onChange={this.handleChange}
                  onDragEnter={this.handleDragEnter}
                  onDragLeave={this.handleDragLeave}
                  accept=".doc, .docx, .gif, .png, .jpg, .jpeg, .pdf"
                  multiple="true"
                  maxSize={999000}
                  style={dropZoneStyle}>
                   {'Click HERE to upload or drop files here...'}
                </DropZone>
                <table id="tblProgress">
                  <tbody>
                    <tr>
                      <td><b><span id="progress">000</span>%</b> <span className="progressBar"><span id="progressBar" /></span></td>
                    </tr>
                  </tbody>
                </table>
              </Col>
          </Row>
        </Form>
        )
      } // close render
    }  // close class
    

    还有 CSS:

    .progressBar {
      background-color: rgba(255, 255, 255, .1);
      width: 100%;
      height: 26px;
    }
    #progressBar {
      background-color: rgba(87, 184, 208, .5);
      content: '';
      width: 0;
      height: 26px;
    }
    

    您缺少的其他功能:

    handleUploadClick(){
      return this.state;
    }
    
    handleChange(){
      this.state.errors.fileError = "";
    }
    
    handleDragEnter(event){
      event.preventDefault();
      document.getElementById("docDZ").classList.add("dragover");
    }
    
    handleDragLeave(event){
      event.preventDefault();
      document.getElementById("docDZ").classList.remove("dragover");
    }
    
    onDropRejected(files){
        const errors ={}
        let isAlertVisible = false;
    
        for(let i=0, j = files.length; i < j; i++){
          const file = files[i];
          const ext = file.name.split('.').pop().toLowerCase();
          //console.log(ext)
    
         if(this.isFileTypeValid(ext)===false){
            errors.fileError = "Only image files (JPG, GIF, PNG), Word files (DOC, DOCX), and PDF files are allowed.";
            isAlertVisible = true;
         }
    
         if(ext === "docx" || ext ==="gif" || ext ==="png" || ext ==="jpg" || ext ==="jpeg" || ext ==="pdf" || ext ==="doc" && file.size > 999000){
          errors.fileError = "Exceeded File Size limit! The maximum file size for uploads is 999 KB.";
          isAlertVisible = true;
        }
    
        this.setState({
          "errors": errors,
          "isAlertVisible": isAlertVisible,
        })
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-02
      • 2021-11-05
      • 2014-07-11
      • 1970-01-01
      • 2021-02-03
      • 1970-01-01
      • 1970-01-01
      • 2011-10-07
      相关资源
      最近更新 更多