使用 ReactJs 将大文件(视频)上传到 nodejs 服务器和 aws s3
Upload Large file (Video) to nodejs server and aws s3 using ReactJs
我正在构建一个 OTT 平台,但在将大文件上传到服务器时遇到问题。
我尝试使用 multer 将文件存储在临时文件夹中并使用 aws-sdk s3.upload
。
它适用于小文件大小,但如果我尝试上传大文件,它 return
Network Error or Error 413 request entity too large
以下错误 413 - 我已更改 nginx.config (client_max_body_size 0;
)
// 0 is for unlimited
但还是没有变化。
我也试过用 multer-s3 来做,但还是没有成功。
后来我尝试与 busboy 一起做,但我仍然面临同样的问题。
在这里,我在使用 busboy 的地方附加了我的代码
在 ReactJs 中我使用 Axios
请帮忙
server.js
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const passport = require('passport');
const helmet = require('helmet');
const path = require('path');
const morgan = require('morgan');
const cors = require('cors');
const dotenv = require('dotenv');
// var admin = require('firebase-admin');
const rateLimit = require('express-rate-limit');
const busboy = require('connect-busboy');
const { setCloudinary } = require('./middleware/cloudinary');
// initalizing app
const app = express();
app.use(cors());
// app.use(helmet());
app.use(
busboy({
highWaterMark: 10 * 1024 * 1024, // Set 10 MiB buffer
})
); // Insert the busboy middle-ware
// for environment files
if (process.env.NODE_ENV === 'production') {
dotenv.config({ path: './env/.env.production' });
} else {
dotenv.config({ path: './env/.env' });
}
const PORT = process.env.PORT || 5000;
const mongoDbUrl = process.env.mongoDbUrl;
const profileRoute = require('./routes/profile');
const adminRoute = require('./routes/admin');
const planRoute = require('./routes/plan');
const videoRoute = require('./routes/video');
//connnecting mongoDB server
mongoose
.connect(mongoDbUrl, {
useNewUrlParser: true,
useFindAndModify: false,
useCreateIndex: true,
useUnifiedTopology: true,
})
.then((result) => {
if (result) {
setCloudinary();
//if all goes right then listing to the server
// var server = https.createServer(options, app);
// console.log(server);
app.listen(PORT, (err) => {
if (err) throw err;
console.log(`server is running at ${PORT}`);
});
}
})
.catch((err) => {
throw err;
});
//logging logs
if (process.env.NODE_ENV === 'production') {
app.use(morgan('tiny'));
} else {
app.use(morgan('dev'));
mongoose.set('debug', true);
}
//initiallizaing passport
app.use(passport.initialize());
// require('./utils/adminRole')(passport);
require('./utils/firebase');
require('./utils/gcm');
app.use(express.json());
app.use(
express.urlencoded({
extended: true,
})
);
// API serving routes
app.use('/api/v1/profile', profileRoute);
app.use('/api/v1/admin', adminRoute);
app.use('/api/v1/plan', planRoute);
app.use('/api/v1/videos', videoRoute);
// FOR REACT JS APP
//if the app is in production then serve files also
// if (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test') {
app.use(express.static(path.join(__dirname, 'client', 'build')));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'client', 'build', 'index.html'));
});
// }
// task
require('./jobs/Jobs');
router.js
router.post('/add/video', (req, res) => {
req.pipe(req.busboy); // Pipe it trough busboy
req.busboy.on('file', (fieldname = 'video', file, filename) => {
console.log(`Upload of '${filename}' started`);
// Create a write stream of the new file
const fstream = fs.createWriteStream(path.join('temp/', filename));
// Pipe it trough
file.pipe(fstream);
// On finish of the upload
fstream.on('close', () => {
console.log(`Upload of '${filename}' finished`);
const childProcess = fork('./processVideo.js', ['message']);
childProcess.on('message', (msg) => res.send(msg));
childProcess.send({ file: fstream, data: req.body });
});
});
});
在 ReactJs 中我使用的是 Axios
const config = {
headers: {
'Content-Type': 'multipart/form-data',
},
onUploadProgress: function (progressEvent) {
var percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
console.log(percentCompleted);
},
};
const formData = new FormData();
formData.append('video', selectedVideo);
formData.append('title', title);
formData.append('description', description);
formData.append('movieCategory', movieCategory);
formData.append('thumbnail', thumbnail);
formData.append('price', price);
formData.append('isPremium', isPremium);
formData.append('quality', quality);
formData.append('language', language);
formData.append('releaseYear', releaseYear);
formData.append('duration', duration);
axios
.post('/api/v1/admin/add/video', formData, config)
.then((res) => {
console.log(res);
alert('File Upload success');
})
.catch((err) => {
console.log(err);
alert('File Upload Error');
});
我建议您使用 presigned S3 上传 link。在这种情况下,服务器仅负责 return 预签名上传 link,并且从客户端代码直接将文件上传到 AWS S3。
您尝试过使用 S3 multi part uploads or potentially transfer accelerator 吗?
我看到你在使用 Express
。您需要设置 Express 请求限制大小。默认值为 100kb
.
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ limit: '50mb', extended: true }));
此外,Multer 的默认文件大小限制也为 1mb
,因此请尝试更改它:
const video_upload = multer({
storage: videoStorage,
fileFilter: videoFilter,
limits: {
fieldSize: '50mb'
}
});
我使用多线程文件上传解决了这个问题。您可以在博客中阅读相关信息。 Here
我正在构建一个 OTT 平台,但在将大文件上传到服务器时遇到问题。
我尝试使用 multer 将文件存储在临时文件夹中并使用 aws-sdk s3.upload
。
它适用于小文件大小,但如果我尝试上传大文件,它 return
Network Error or Error 413 request entity too large
以下错误 413 - 我已更改 nginx.config (client_max_body_size 0;
)
// 0 is for unlimited
但还是没有变化。 我也试过用 multer-s3 来做,但还是没有成功。 后来我尝试与 busboy 一起做,但我仍然面临同样的问题。 在这里,我在使用 busboy 的地方附加了我的代码 在 ReactJs 中我使用 Axios 请帮忙
server.js
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const passport = require('passport');
const helmet = require('helmet');
const path = require('path');
const morgan = require('morgan');
const cors = require('cors');
const dotenv = require('dotenv');
// var admin = require('firebase-admin');
const rateLimit = require('express-rate-limit');
const busboy = require('connect-busboy');
const { setCloudinary } = require('./middleware/cloudinary');
// initalizing app
const app = express();
app.use(cors());
// app.use(helmet());
app.use(
busboy({
highWaterMark: 10 * 1024 * 1024, // Set 10 MiB buffer
})
); // Insert the busboy middle-ware
// for environment files
if (process.env.NODE_ENV === 'production') {
dotenv.config({ path: './env/.env.production' });
} else {
dotenv.config({ path: './env/.env' });
}
const PORT = process.env.PORT || 5000;
const mongoDbUrl = process.env.mongoDbUrl;
const profileRoute = require('./routes/profile');
const adminRoute = require('./routes/admin');
const planRoute = require('./routes/plan');
const videoRoute = require('./routes/video');
//connnecting mongoDB server
mongoose
.connect(mongoDbUrl, {
useNewUrlParser: true,
useFindAndModify: false,
useCreateIndex: true,
useUnifiedTopology: true,
})
.then((result) => {
if (result) {
setCloudinary();
//if all goes right then listing to the server
// var server = https.createServer(options, app);
// console.log(server);
app.listen(PORT, (err) => {
if (err) throw err;
console.log(`server is running at ${PORT}`);
});
}
})
.catch((err) => {
throw err;
});
//logging logs
if (process.env.NODE_ENV === 'production') {
app.use(morgan('tiny'));
} else {
app.use(morgan('dev'));
mongoose.set('debug', true);
}
//initiallizaing passport
app.use(passport.initialize());
// require('./utils/adminRole')(passport);
require('./utils/firebase');
require('./utils/gcm');
app.use(express.json());
app.use(
express.urlencoded({
extended: true,
})
);
// API serving routes
app.use('/api/v1/profile', profileRoute);
app.use('/api/v1/admin', adminRoute);
app.use('/api/v1/plan', planRoute);
app.use('/api/v1/videos', videoRoute);
// FOR REACT JS APP
//if the app is in production then serve files also
// if (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test') {
app.use(express.static(path.join(__dirname, 'client', 'build')));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'client', 'build', 'index.html'));
});
// }
// task
require('./jobs/Jobs');
router.js
router.post('/add/video', (req, res) => {
req.pipe(req.busboy); // Pipe it trough busboy
req.busboy.on('file', (fieldname = 'video', file, filename) => {
console.log(`Upload of '${filename}' started`);
// Create a write stream of the new file
const fstream = fs.createWriteStream(path.join('temp/', filename));
// Pipe it trough
file.pipe(fstream);
// On finish of the upload
fstream.on('close', () => {
console.log(`Upload of '${filename}' finished`);
const childProcess = fork('./processVideo.js', ['message']);
childProcess.on('message', (msg) => res.send(msg));
childProcess.send({ file: fstream, data: req.body });
});
});
});
在 ReactJs 中我使用的是 Axios
const config = {
headers: {
'Content-Type': 'multipart/form-data',
},
onUploadProgress: function (progressEvent) {
var percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
console.log(percentCompleted);
},
};
const formData = new FormData();
formData.append('video', selectedVideo);
formData.append('title', title);
formData.append('description', description);
formData.append('movieCategory', movieCategory);
formData.append('thumbnail', thumbnail);
formData.append('price', price);
formData.append('isPremium', isPremium);
formData.append('quality', quality);
formData.append('language', language);
formData.append('releaseYear', releaseYear);
formData.append('duration', duration);
axios
.post('/api/v1/admin/add/video', formData, config)
.then((res) => {
console.log(res);
alert('File Upload success');
})
.catch((err) => {
console.log(err);
alert('File Upload Error');
});
我建议您使用 presigned S3 上传 link。在这种情况下,服务器仅负责 return 预签名上传 link,并且从客户端代码直接将文件上传到 AWS S3。
您尝试过使用 S3 multi part uploads or potentially transfer accelerator 吗?
我看到你在使用 Express
。您需要设置 Express 请求限制大小。默认值为 100kb
.
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ limit: '50mb', extended: true }));
此外,Multer 的默认文件大小限制也为 1mb
,因此请尝试更改它:
const video_upload = multer({
storage: videoStorage,
fileFilter: videoFilter,
limits: {
fieldSize: '50mb'
}
});
我使用多线程文件上传解决了这个问题。您可以在博客中阅读相关信息。 Here