从blob转换为二进制文件以将其保存到mongodb [英] converting from blob to binary to save it to mongodb

查看:187
本文介绍了从blob转换为二进制文件以将其保存到mongodb的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我从前端到我的节点快递服务器都得到了这样的对象.

I get the object like that from the front end to my node express server.

{ 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的函数

this is my function where i save to the 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发送.

some how i need to convert the image i am receiving to be binary in order to save. or i need to send it as a binary base64 from the client side it self.

我的客户端使用React Redux Dropzone发送数据.

my client side i use react redux Dropzone for sending my data.

这是我的表格及其外观.

here is my form and how it look like.

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的服务功能.

this is my service function which deals with the rest 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)
  });
};

推荐答案

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

So I think your problem is in this line:

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

这是您要插入data的mongoDB表列,从而给您带来该错误.它需要一个字符串或Buffer,并且您给了它一个File对象.

And it's the mongoDB table column that you are inserting data into that is giving you that error. It's expecting a string or Buffer and you gave it a File object.

您可以使用得到的base64字节字符串,下面我将尝试与您的代码集成:

You can get a base64 byte string using what I posted here, which I'll try and integrate with your code, below:

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

You'd need to ensure you have a variable to collect your file(s). This is how I have the top of my page set up:

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看起来像这样:

Then we work on supplying the bytes to attachments and sending it to new_project.picture.data. For me, I use a function that runs onDrop of the DropZone using onDrop={this.uploadFile}. I can't really tell what you're doing because I have no clue what filesToUpload refers to. My uploadFile looks like this:

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函数:

and this would be the getData function ran for each file dropped/added to the DropZone:

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.
  }
}

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

Once you have that, you should be able to do:

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

如果不是,由于某种原因,您可能会尝试在exports.create_a_project()内部调用它,这是您做的第一件事:

If not, for some reason, you might try to call this inside exports.create_a_project(), as the first thing you do:

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

这甚至可以工作,而不必根据已有的内容修改onDrop例程.如果this.state.attachments没有任何内容,则SomeStore.state.attachments绝对应该,假设您已将其保存为名为someStore.jsx的名为stores的文件夹.

This might even work without having to modify your onDrop routine from what you have. And if this.state.attachments doesn't have anything, your SomeStore.state.attachments definitely should, assuming you have this saved to a folder called stores as 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&适用的进度指示器标记​​:

And my DropZone & applicable progress indicator markup:

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,
    })
  }
}

这篇关于从blob转换为二进制文件以将其保存到mongodb的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆