我正在尝试在nodejs/rect应用程序中上传一个文件。文件的上传工作得很好,但当我想得到await.post()
请求的响应时,我遇到了一些问题。当我上传文件时,我制作了一个微调器来显示文件正在上传,我希望这个微调器在上传完成时停止,然后显示一条消息通知用户加载完成。
我解释这些问题,因为它有点复杂。
当我在本地工作时:
两分钟后(我计时以理解(,浏览器控制台显示错误消息net::ERR_EMPTY_RESPONSE
。但在服务器端,一切都在继续,请求顺利结束。但在前端,即使请求结束,当我的post请求向我的客户端发送res消息时,我的微调器也不会停止转动。
我以为这是关于我的服务器超时的问题,所以我做了很多测试,把setTimeout放在我的服务器上:
var server = app.listen(PORT, () =>
console.log(`Server started on port ${PORT}`)
);
server.timeout = 3840000;
在我的路线请求中:
router.post('/', async (req, res) => {
req.setTimeout(3600000);
甚至我也试图更改客户端axios.post
函数的超时:
const res = await axios.post('/api/students', newFile, {
timeout: 3600000,
});
但没有什么能解决我的问题。
奇怪的是,当应用程序托管时,问题却不同了!
事实上,没有错误消息,但在1分钟后,微调器无故停止。
我做了很多研究,但没有什么能回答我的问题,我已经研究了几天了,我不明白为什么。我想可能是代理问题或浏览器超时,但我不知道。。。
如果你有一个小线索,它可以帮我很多!谢谢你的帮助!
更新:
我的/api/学生路线的代码
const express = require('express');
const router = express.Router();
const fileUpload = require('express-fileupload');
const Student = require('../../models/Student');
const CSVToJSON = require('csvtojson');
const utf8 = require('utf8');
var accents = require('remove-accents');
const NodeGeocoder = require('node-geocoder');
const sleep = require('util').promisify(setTimeout);
let msg = 'Etudiants ajoutés à la base de données';
//The column that we want to keep in the database
const goodColumn = [
'Civilite',
'Nom_patronymique',
'Prenom',
'Date_naissance',
'No_etudiant',
'Libelle_nationalite',
'Telephone_portable',
'Mailum',
'Adresse_fixe_postal',
'Adresse_fixe_ville',
'Libelle_etape',
];
// Set up the environnement for geocoding the adress
const options = {
provider: 'openstreetmap',
};
const geoCoder = NodeGeocoder(options);
//FUNCTION TO VERIFY IF A STRING HAS A NUMBER IN IT
function hasNumber(myString) {
return /d/.test(myString);
}
router.post('/', (req, res, next) => {
// This should be BEFORE `fileUpload`
req.setTimeout(0);
next();
});
router.use(fileUpload());
//@route POST api/students
//@desc Fill the database with the json information
//@access Public
router.post('/', async (req, res, next) => {
//FORMER FILES ROUTES
//take the information
const buf = Buffer.from(req.files.file.data).toString();
//CONVERSION CSV STRING TO JSON
CSVToJSON()
.fromString(buf)
.then((source) => {
for (let i = 0; i < source.length; i++) {
for (let j = 0; j < Object.keys(source[i]).length; j++) {
const columnName = Object.keys(source[i]);
columnName.forEach((element) => {
if (!goodColumn.includes(element)) {
delete source[i][element];
}
if (element == 'Libelle_etape') {
const str = source[i]['Libelle_etape'];
const result = accents.remove(utf8.decode(str));
source[i]['Libelle_etape'] = result;
}
});
}
}
data = JSON.stringify(source);
datajson = JSON.parse(data);
//USE THE FUNCTION TO PUT THE DATA IN THE DB
insertIntoDataBase(datajson);
});
// CLEAR TABLE BEFORE ADD NEW STUDENTS FROM FILE
Student.deleteMany({}, function (err) {});
//ROUTES STUDENTS - FUNCTION TO PUT THE JSON DATA IN THE DATABASE
async function insertIntoDataBase(jsonString) {
for (let i = 0; i < jsonString.length; i++) {
console.log(`boucle ${i}`);
try {
//READ DATA FROM DE CSV FILE (already convert into json data) AND PUT IT INTO VARIABLES
let {
Civilite,
Nom_patronymique,
Prenom,
Date_naissance,
No_etudiant,
Libelle_nationalite,
Telephone_portable,
Mailum,
Adresse_fixe_postal,
Adresse_fixe_ville,
Libelle_etape,
} = jsonString[i];
console.log(Nom_patronymique + ' ' + Prenom);
// VERIFICATION VILLE STRING FORMAT ( AVOIR NUMBER OR ..EME)
if (hasNumber(Adresse_fixe_ville)) {
Adresse_fixe_ville = Adresse_fixe_ville.replace(/[0-9]/g, '');
if (Adresse_fixe_ville.endsWith(' EME')) {
Adresse_fixe_ville = Adresse_fixe_ville.substring(
0,
Adresse_fixe_ville.length - 4
);
}
}
//VERIFICATION OF THE PHONE NUMBER - if empty attributes a default value
if (Telephone_portable !== undefined && Telephone_portable.length < 1) {
Telephone_portable = '0000000000';
}
// GEOCODING THE ADDRESS TO CONVERT INTO LATITUDE AND LONGITUDE
geoCoder
.geocode({
city: Adresse_fixe_ville,
zipcode: Adresse_fixe_postal,
})
.then(async (res) => {
//TEST
var Latitude;
var Longitude;
if (res[0] !== undefined) {
Latitude = res[0].latitude;
Longitude = res[0].longitude;
} else {
console.log(Adresse_fixe_ville);
Latitude = 0.0;
Longitude = 0.0;
}
//CREATE A STUDENT WITH THE INFO + LAT AND LONG
student = new Student({
Civilite,
Nom_patronymique,
Prenom,
Date_naissance,
No_etudiant,
Libelle_nationalite,
Telephone_portable,
Mailum,
Adresse_fixe_postal,
Adresse_fixe_ville,
Libelle_etape,
Latitude,
Longitude,
});
//VERIFICATION IF ALL THE ATTRIBUTE OF THE STUDENT ARE OK - IF NOT : undefined
if (
!(
student.Civilite === undefined ||
student.Nom_patronymique === undefined ||
student.Prenom === undefined ||
student.Date_naissance === undefined ||
student.No_etudiant === undefined ||
student.Libelle_nationalite === undefined ||
student.Telephone_portable === undefined ||
student.Mailum === undefined ||
student.Adresse_fixe_postal === undefined ||
student.Adresse_fixe_ville === undefined ||
student.Libelle_etape === undefined
)
) {
//SAVE THE STUDENT IN THE DATABASE
await student.save();
} else {
res.status(500);
msg =
'Le fichier csv téléchargé est au mauvais format de données';
}
})
.catch((err) => {
console.log(err);
});
} catch (err) {
console.log(err.message);
res.status(500);
msg =
'Erreur serveur, veuillez réessayer avec un fichier csv au bon format';
return;
}
//WAIT FOR GEOCODER - 1,2 second
await sleep(1200);
}
//COUNT A MANY STUDENT WE HAVE IN THE DATABASE
Student.find().exec(function (err, results) {
var count = results.length;
res.json({ msg: msg, count: count });
});
}
});
//@route GET api/students
//@desc Return all the students in the database (all information / attributes)
//@access Public
router.get('/', function (req, res, next) {
Student.find(function (err, students) {
if (err) {
res.send(err);
}
res.json(students);
});
});
module.exports = router;
在节点< 13.0.0
中,默认的套接字超时为2分钟,因此两分钟后套接字将关闭,您将在客户端上获得ERR_EMPTY_RESPONSE
,因为套接字已关闭,而不允许您响应该请求。
从Node13.0.0
,默认超时设置为0
(无超时(,这可能是部署应用程序时它工作的原因,您的服务器可能正在运行Node>= 13.0.0
。
如果在使用值大于2分钟的req.setTimeout
之后,仍然存在此问题,则可能是因为您没有正确结束请求或在错误的位置使用了req.setTimeout
。
这是一个巨大的文件,它是一个带有1300行的csv,大约需要上传 30分钟
根据您的评论,我可以告诉您,您路由中的req.setTimeout
没有按您的意愿执行,因为在此之前您有一个中间件,可能是multer
,因为您的代码没有将req.setTimeout
设置为>30 min
。
因此,请求在到达您的路线之前就超时了。你应该这样做:
app.post(
'/',
(req, res, next) => {
// Set request setTimeout BEFORE any other middlewares
req.setTimeout(ms('35m')); // using `ms` package
next();
},
upload.single('file'), // or whatever multer setup you have
(req, res, next) => {
// Your route
}
)
有了这个代码,你有35分钟的时间上传&结束您的响应,因为它不会在2分钟后在multer
中间件上超时。当然,如果你不知道需要多少时间,很可能是在这种情况下,你可以使用req.setTimeout(0)
或你认为合适的任何值。
更新
对于您的代码,您必须执行以下操作:
// or router.use((req, res, next) => ...)
router.post('/', (req, res, next) => {
// This should be BEFORE `fileUpload`
req.setTimeout(0);
next();
});
router.use(fileUpload());
//@route POST api/students
//@desc Fill the database with the json information
//@access Public
router.post('/', async (req, res, next) => {
// ...rest of your code
})