用Nodejs写了个HTTP文件服务器,好蛋疼

用Nodejs写了个HTTP文件服务器,好蛋疼

现在express里有个文件名的bug, res.sendfile, res.download之类的都有, 不支持路径中有中文名称包括文件名 后端用mongodb存文件全路径,下载时只通过id传, 原来想直接传全文件路径比如C:\works\a.txt,后来发现不行,好象security限制住了,根本不会route到/download/:filename的处理,直接显示无法GET…

主要两个页面,一个Add file, 一个显示所有文件,点击任意一个即可下载

exports.down = function(req, res){

var MongoClient = require('mongodb').MongoClient;
var fs = require('fs');
var path = require('path');
var ObjectID = require('mongodb').ObjectID;

MongoClient.connect(“mongodb://localhost:27017/filelist”, function(err, db) { if (!err) { console.log(“We are connected.”); } var fileid = req.params.id;

var collection = db.collection('filelink');
collection.findOne({"_id": ObjectID(fileid)}, function(err, item) {
	var filepath = item["file"];
	var filebase = path.basename(filepath);
	//console.log(filepath);
	//console.log(filebase);
	res.download(filepath,filebase);


});

});

};


9 回复

用Node.js写了个HTTP文件服务器,好蛋疼

最近我在使用Node.js编写一个HTTP文件服务器时遇到了一些问题,尤其是在处理路径中包含中文字符的文件时。虽然Express框架提供了res.sendfileres.download等方法,但它们并不支持路径中含有中文字符的情况。

此外,在后端使用MongoDB存储文件的全路径时,我发现不能直接将全路径传递给客户端进行下载。例如,尝试通过C:\works\a.txt这样的路径直接传递文件时,会遇到安全限制,导致请求无法到达指定的路由。

我设计的主要有两个页面:一个用于添加文件,另一个用于显示所有文件并允许用户下载。以下是相关的代码示例:

路由配置

首先,我们定义一个路由来处理文件下载请求:

const express = require('express');
const app = express();
const MongoClient = require('mongodb').MongoClient;
const path = require('path');

app.get('/download/:id', exports.down);

// 其他路由和中间件配置

文件下载处理函数

接下来是处理文件下载的核心逻辑:

exports.down = function(req, res) {
    MongoClient.connect("mongodb://localhost:27017/filelist", (err, db) => {
        if (err) {
            console.error("数据库连接失败:", err);
            return res.status(500).send("Database connection error");
        }
        console.log("We are connected.");

        const fileid = req.params.id;
        const collection = db.collection('filelink');

        collection.findOne({ "_id": new MongoClient.ObjectID(fileid) }, (err, item) => {
            if (err) {
                console.error("查询文件信息失败:", err);
                return res.status(500).send("Failed to find file");
            }

            const filepath = item["file"];
            const filebase = path.basename(filepath);

            // 使用res.download方法下载文件
            res.download(filepath, filebase, (err) => {
                if (err) {
                    console.error("文件下载失败:", err);
                    res.status(500).send("File download failed");
                }
                db.close();
            });
        });
    });
};

总结

以上代码展示了如何在Node.js中通过Express框架实现一个简单的HTTP文件服务器,并处理文件下载功能。需要注意的是,路径中的中文字符可能会引起问题,需要确保路径编码正确。另外,数据库操作应确保异常处理,以避免意外情况导致服务中断。


标题是lz自嘲?

楼主不蛋疼,谁蛋疼! 一个简单的文件服务器都要搬出mongodb

大炮打蚊子

要保存状态啊,象HFS一样, 如果没有persistence, 下次开启时又没有了,上次share的列表, 你用文件去存一个列表也是一样。。。。 这个是可以共享任意磁盘上的文件的,不是那个public文件夹share, 设置一下就可以了

我也觉得,原来想用sqlite3, 结果那个傻X的sqlite npm package装不起来, 老是在node-gyp编译时出错 有空再试试

发一下写来自己用的非常简单的静态文件服务器,不需要express,mongodb,sqlite…

var PORT = 8090;
var MIME = {
    'htm':  'text/html',
    'html': 'text/html',
    'css':  'text/css',
    'gif':  'image/gif',
    'ico':  'image/x-icon',
    'jpg':  'image/jpeg',
    'js':   'text/javascript',
    'png':  'image/png',
    'rar':  'application/x-rar-compressed',
    'txt':  'text/plain',
    'json': 'text/plain',
    'jar':  'application/java-archive'
};
var dir = process.argv[2];
var ROOT = dir ? dir : process.cwd();

var http = require(‘http’); var url = require(‘url’); var fs = require(‘fs’); var path = require(‘path’);

http.createServer(function(request, response) { var pathname = url.parse(request.url).pathname; var realpath = pathname !== ‘/’ ? ROOT + pathname : __filename; var extname = path.extname(realpath).slice(1); var contentType = ‘text/plain’;

if (extname && MIME[extname]) {
    contentType = MIME[extname];
}

fs.exists(realpath, function(exists) {
    if (exists) {
        fs.readFile(realpath, function(err, data) {
            if (err) throw err;

            response.writeHead(200, {'Content-Type': contentType});
            response.write(data);
            response.end();
        });
    } else {
        response.writeHead(404, {'Content-Type': 'text/plain'});
        response.write('Not Found');
        response.end();
    }
});

}).listen(PORT);

console.log('simple static file server runing at port: ’ + PORT + ‘.’);

__filename  不如改成index.html

针对你的问题,确实存在一些关于路径处理和文件下载的安全性问题。这里提供一种解决方案,使用 res.sendFile 方法来代替 res.download,并且确保路径是安全的。

以下是修改后的代码示例:

const express = require('express');
const fs = require('fs');
const path = require('path');
const MongoClient = require('mongodb').MongoClient;

const app = express();

app.get('/download/:id', (req, res) => {
    const fileId = req.params.id;
    const dbUrl = "mongodb://localhost:27017/filelist";
    
    MongoClient.connect(dbUrl, { useNewUrlParser: true, useUnifiedTopology: true }, (err, db) => {
        if (err) {
            return res.status(500).send({ error: 'Database connection failed' });
        }

        const dbo = db.db("filelist");
        const collection = dbo.collection('filelink');

        collection.findOne({ "_id": new ObjectID(fileId) }, (err, result) => {
            if (err || !result) {
                return res.status(404).send({ error: 'File not found' });
            }
            
            const filePath = decodeURIComponent(result.file);
            const fileName = path.basename(filePath);

            if (!fs.existsSync(filePath)) {
                return res.status(404).send({ error: 'File does not exist' });
            }

            res.sendFile(filePath, { root: __dirname }, (err) => {
                if (err) {
                    console.error(err);
                    res.status(err.status).end();
                } else {
                    console.log(`Sent: ${filePath}`);
                }
            });
        });

        db.close();
    });
});

app.listen(3000, () => {
    console.log('Server running on port 3000');
});

关键点解释:

  1. 路径解码decodeURIComponent 用于解码可能包含特殊字符(如中文)的路径。
  2. 文件存在检查:在发送文件之前,先检查文件是否存在。
  3. 使用 res.sendFile:该方法支持传递根目录参数,并且可以处理路径中的特殊字符。

这样处理后,可以避免由于路径编码问题导致的文件找不到或不能下载的情况。同时,也保证了安全性。

回到顶部