我如何在nodeJS或Express中创建一次性下载链接?
我正在努力找到实现这一目标的最简单方法。我到目前为止的想法是:
使用fs流读取然后删除文件或以某种方式生成一个单击下载按钮后删除的链接/路由
这些实现是否可行?有更简单的方法吗?
任何帮助或示例代码将不胜感激!
-谢谢
检查这个简单的实现:
您将下载的信息存储在一个文件中。文件名是下载会话ID。文件内容是要下载的文件的真实路径。
使用这三个函数来管理下载会话的生命周期:
var fs = require('fs');
var crypto = require('crypto');
var path = require('path');
// Path where we store the download sessions
const DL_SESSION_FOLDER = '/var/download_sessions';
/* Creates a download session */
function createDownload(filePath, callback) {
// Check the existence of DL_SESSION_FOLDER
if (!fs.existsSync(DL_SESSION_FOLDER)) return callback(new Error('Session directory does not exist'));
// Check the existence of the file
if (!fs.existsSync(filePath)) return callback(new Error('File doest not exist'));
// Generate the download sid (session id)
var downloadSid = crypto.createHash('md5').update(Math.random().toString()).digest('hex');
// Generate the download session filename
var dlSessionFileName = path.join(DL_SESSION_FOLDER, downloadSid + '.download');
// Write the link of the file to the download session file
fs.writeFile(dlSessionFileName, filePath, function(err) {
if (err) return callback(err);
// If succeeded, return the new download sid
callback(null, downloadSid);
});
}
/* Gets the download file path related to a download sid */
function getDownloadFilePath(downloadSid, callback) {
// Get the download session file name
var dlSessionFileName = path.join(DL_SESSION_FOLDER, downloadSid + '.download');
// Check if the download session exists
if (!fs.existsSync(dlSessionFileName)) return callback(new Error('Download does not exist'));
// Get the file path
fs.readFile(dlSessionFileName, function(err, data) {
if (err) return callback(err);
// Return the file path
callback(null, data);
});
}
/* Deletes a download session */
function deleteDownload(downloadSid, callback) {
// Get the download session file name
var dlSessionFileName = path.join(DL_SESSION_FOLDER, downloadSid + '.download');
// Check if the download session exists
if (!fs.existsSync(dlSessionFileName)) return callback(new Error('Download does not exist'));
// Delete the download session
fs.unlink(dlSessionFileName, function(err) {
if (err) return callback(err);
// Return success (no error)
callback();
});
}
使用createDownload()
在任何需要的地方创建下载会话。它返回下载sid,然后您可以使用它来构建下载URL,如:http://your.server.com/download?sid=<RETURNED SID>
。
最后,您可以为/download
路线添加一个简单的处理程序:
app.get('/download', function(req, res, next) {
// Get the download sid
var downloadSid = req.query.sid;
// Get the download file path
getDownloadFilePath(downloadSid, function(err, path) {
if (err) return res.end('Error');
// Read and send the file here...
// Finally, delete the download session to invalidate the link
deleteDownload(downloadSid, function(err) {
// ...
});
});
});
使用此方法,您不必创建/移动/删除大型下载文件,这可能会导致响应缓慢和不必要的资源消耗。
您可以从app.routes对象中删除路由。有关更多信息,请参阅Remove route mappings in NodeJS Express。
这是我快速而且没有经过良好测试的方式来做你所要求的:
var express = require('express');
var app = express();
app.get('/download', function(req,res,next){
res.download('./path/to/your.file');
//find this route and delete it.
for(i = 0; i < app.routes.get.length; i++){
if(app.routes.get[i].path === '/download'){
app.routes.get.splice(i,1);
}
}
});
app.listen(80);
我可能会映射一条路线来管理下载,然后在下载文件时移动或删除它。这样我可以防止大量的路线兑现,或者来自其他两个答案的大量小临时文件,但是YMMV。像这样的东西:
// say your downloads are in /downloads
app.get('/dl/:filename', function(req, res) {
var fileStream = fs.createReadStream('/downloads' + req.params.filename);
// error handler, ie. file not there...
fileStream.on('error', function(err) {
if(err) {
res.status(404); // or something
return res.end();
}
});
// here you ow pipe that stream to the response,
fileStream.on('data', downloadHandler);
// and here delete the file or move it to other folder or whatever, do cleanup
fileStream.on('end', deleteFileHandler);
}
注意:这是一个可能的安全漏洞,它可以让攻击者将文件下载到您的下载位置之外。那个filename
param直接传递给fs
。