我从前端到我的节点快速服务器得到了这样的对象。
{ 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)
});
};
答案 0 :(得分:0)
所以我认为你的问题就在这一行:
data
你正在插入{m}的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
运行DropZone onDrop={this.uploadFile}
的函数。我无法真正告诉你他在做什么,因为我不知道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);
}
然后processFile()
运行onload
:
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&amp;适用的进度指标标记:
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,
})
}
}