我正在与Multer
和Redux
一起学习React
。
我的express
路由器就像
router.post('/upload', addressController.uploadImage);
我的Multer
代码如下所示
const uploadImage = (req, res, next) => {
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, './uploads/');
},
filename: function(req, file, cb) {
cb(null, Date.now() + '-' + file.originalname);
}
});
const fileFilter = (req, file, cb) => {
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, true);
} else {
cb(new Error('Try to upload .jpeg or .png file.'), false);
}
};
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: fileFilter
}).single('addressImage');
upload(req, res, function(error) {
if (error) {
// An error occurred when uploading
res.status(500).json({
message: error // I would like to send error from Here.
});
console.log(error);
} else {
if (req.file.filename === res.req.res.req.file.filename) {
res.status(200).json({
message: 'File uploaded',
file: req.file.filename
});
}
return;
}
});
}
我的动作如下所示
export const uploadImage = (formData, id, config) => dispatch => {
return Axios.post('/api/address/upload', formData, config)
.then(response => {
dispatch({
type: 'uploadImage',
payload: response.data
});
})
.catch(error => {
dispatch({
type: 'uploadImage',
payload: error // I would like to pass error through here.
});
return false;
});
};
我的减速机如下
const addressReducer = (state = initialState, action) => {
switch (action.type) {
case 'getAddresses': {
return {
...state,
controlModal: action.payload.valueModal,
address: action.payload.addressData
};
}
case 'uploadImage': {
return {
...state,
uploadImage: action.payload
};
}
default:
return state;
}
};
我想在我的组件中出现错误,如下所示
render() {
console.log(this.props.uploadImage);
}
const mapStateToProps = state => ( {
uploadImage: state.addressReducer.uploadImage
} );
export default connect(mapStateToProps)(ModalElement);
我的控制台输出如下
当我尝试上传不带.jpeg和.png扩展名的文件时,如何在React组件中出现Try to upload .jpeg or .png file.
错误?
答案 0 :(得分:4)
您不必发送500状态代码,而应该发送400
res.status(400).json({
message: error // I would like to send error from Here.
});
答案 1 :(得分:4)
Error
在通过res.json()
传递时无法解析为有效的json,因此将其剥离。
因此,要访问消息"Try to upload .jpeg or .png file."
,您应该像这样更新Multer
代码:
if (error) {
// An error occurred when uploading
res.status(500).json({
/** error.message => "Try to upload .jpeg or .png file." */
message: error.message // I would like to send error from Here.
});
console.log(error);
}
如果您尝试使用Postman上传文件,则会收到以下API响应:
{
"message": "Try to upload .jpeg or .png file."
}
一旦有了,就可以像这样更改dispatch()
:
.catch(error => {
dispatch({
type: "uploadImage",
/** error.data is the response. We want the `message` property from it */
payload: error.data.message // I would like to pass error through here.
});
return false;
});
答案 2 :(得分:3)
这是我为与主应用程序一起创建的头像微服务完成此工作的方式。
警告:此说明贯穿于整个流程,因此,如果您已经理解的话,它可能是冗长且多余的。
首先,您必须创建一个axios
配置。默认情况下,axios
不会显示服务器返回的err
,而只会显示通用的Error
对象。您需要设置 interceptor
。
utils / axiosConfig.js
import get from 'lodash/get';
import axios from 'axios';
export const avatarAPI = axios.create({
baseURL: 'http://localhost:4000/api/', // this makes it easier so that any request will be prepended with this baseURL
});
avatarAPI.interceptors.response.use(
response => response, // returns the server response
error => {
const err = get(error, ['response', 'data', 'err']); // this checks if "error.response.data.err" is present (this is the error returned from the server); VERY IMPORTANT: this "err" property is specified in our express middlewares/controllers, so please pay attention to the naming convention.
return err ? Promise.reject(err) : Promise.reject(error.message); // if the above is present, return the server error, else return a generic Error object
},
);
用户使用formData
提交表单,这会触发action
创建者:
uploadAvatar重击动作创建者(这是等待我们服务器中的response
或error
的诺言):
import { avatarAPI } from '../utils/axiosConfig'; // import the custom axios configuration that was created above
import * as types from 'types';
const uploadAvatar = formData => dispatch =>
avatarAPI
.post(`avatar/create`, formData) // this makes a POST request to our server -- this also uses the baseURL from the custom axios configuration, which is the same as "http://localhost:4000/api/avatar/create"
.then(({ data }) => {
dispatch({ type: types.SET_CURRENT_AVATAR, payload: data.avatarurl });
})
.catch(err => // this will return our server "err" string if present, otherwise it'll return a generic Error object. IMPORTANT: Just in case we get a generic Error object, we'll want to convert it to a string (otherwise, if it passes the generic Error object to our reducer, stores it to redux state, passes it to our connected component, which then tries to display it... it'll cause our app to crash, as React can't display objects)
dispatch({ type: types.SERVER_ERROR, payload: err.toString() }),
);
我们的POST
路线收到了express
请求:
app.post('/api/avatar/create', saveImage, create);
请求到达以下路径:'/api/avatar/create'
,先通过中间件功能(见下文),然后再通过另一saveImage
中间件功能,最后再通过create
控制器。
服务器将响应发送回客户端。我们服务器的响应通过axios
配置interceptor
传递,该配置确定如何处理我们服务器返回的response
或error
。然后它将response
或error
传递给.then()
创建者的.catch()
或action
。 action
创建者将其移交给reducer
,后者会更新redux
状态,然后会更新connect
ed组件。
无论您要定义express
中间件(例如bodyParser
,cors
还是passport
等),都需要创建一个{{1} }中间件功能(每次上传文件时,文件会先通过此功能 first ):
middlewares / index.js
multer
services / saveImage.js (在通过上面的中间件函数后,结果将传递到此app.use(cors({ origin: "http://localhost:3000" }));
app.use(bodyParser.json());
app.use(
multer({
limits: {
fileSize: 10240000,
files: 1,
fields: 1
},
fileFilter: (req, file, next) => {
if (!/\.(jpe?g|png|gif|bmp)$/i.test(file.originalname)) {
req.err = "That file extension is not accepted!"; // this part is important, I'm attaching the err to req (which gets passed to the next middleware function => saveImage)
next(null, false);
}
next(null, true);
}
}).single("file")
);
...etc
中间件函数)
saveImage
如果以上通过,则它将const fs = require("fs");
const sharp = require("sharp");
const { createRandomString } = require('../../utils/helpers');
module.exports = (req, res, next) => {
// if the file failed to pass the middleware function above, we'll return the "req.err" as "err" or return a string if "req.file" is undefined. In short, this returns an "error.response.data.err" to the client.
if (req.err || !req.file) {
return res.status(400).json({ err: req.err || "Unable to process file." });
}
const randomString = createRandomString();
const filename = `${Date.now()}-${randomString}-${req.file.originalname}`;
const filepath = `uploads/${filename}`;
const setFile = () => {
req.file.path = filepath;
return next();
};
/\.(gif|bmp)$/i.test(req.file.originalname)
? fs.writeFile(filepath, req.file.buffer, (err) => {
if (err) return res.status(400).json({ "Unable to process file." });
setFile();
})
: sharp(req.file.buffer)
.resize(256, 256)
.max()
.withoutEnlargement()
.toFile(filepath)
.then(() => setFile());
};
(包含req
及其所有属性)传递给req.file
控制器,在我的情况下,该控制器存储文件的路径( /uploads/name-of-file.ext),以及将图像(http://localhost:4000/uploads/name-of-file.ext)检索到我的数据库的字符串。在我的情况下,该字符串然后被发送回客户端以存储为redux状态,然后作为用户的化身进行更新(当将字符串传递到create
时,它向<img src={avatarurl} alt="avatarurl.png" />
发出一个请求微服务)。
可以说用户尝试上传GET
图片。它通过我们的.tiff
multer中间件函数传递,该函数触发express
错误,此错误通过"That file extension is not accepted!"
返回到req.err
,后者返回saveImage
为: req.err
在我们的客户端,return res.status(400).json({ err: req.err });
流经我们的axios err
:
interceptor
avatarAPI.interceptors.response.use(
response => response,
error => {
const err = get(error, ['response', 'data', 'err']); // this checks if "error.response.data.err" is present; which it is, and is now "That file extension is not accepted!"
return err ? Promise.reject(err) : Promise.reject(error.message); // that err string gets returned to our uploadAvatar action creator's "catch" block
},
);
动作创建者的uploadAvatar
块被触发:
catch
.catch(err => // our server "err" is passed to here from the interceptor
dispatch({ type: types.SERVER_ERROR, payload: err.toString() }), // then that "err" is passed to a reducer
);
拿起服务器reducer
并将其存储到状态:
err
一个import * as types from 'types';
const serverInitialState = {
error: '',
message: '',
};
const ServerReducer = (state = serverInitialState, { payload, type }) => {
switch (type) {
case types.RESET_SERVER_MESSAGES:
return { ...state, error: '' };
case types.SERVER_ERROR:
return { ...state, error: payload }; // the server err is stored to redux state as "state.server.error"
case types.SERVER_MESSAGE:
return { ...state, message: payload };
default:
return state;
}
};
export default ServerReducer;
版本的组件检索到此connect
并显示它(不必担心这里的逻辑,只是它是一个将state.server.error
显示为{{1 }}):
state.server.error
最终结果是向用户显示serverError
错误: