Javascript Node Express 中的 res.sendfile 与传递数据
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33027089/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
res.sendfile in Node Express with passing data along
提问by user3393991
Is there any way to redirect to an HTML file from a Node.JS application with something like: res.sendFile
of express and pass a JSON data along to the html file?
有什么方法可以从 Node.JS 应用程序重定向到 HTML 文件,例如:res.sendFile
of express 并将 JSON 数据传递给 html 文件?
采纳答案by jfriend00
You get one response from a given request. You can either combine multiple things into one response or require the client to make separate requests to get separate things.
您从给定的请求中得到一个响应。您可以将多个事物组合成一个响应,也可以要求客户端发出单独的请求以获取单独的事物。
If what you're trying to do is to take an HTML file and modify it by inserting some JSON into it, then you can't use just res.sendFile()
because that just reads a file from disk or cache and directly streams it as the response, offering no opportunity to modify it.
如果您想要做的是获取一个 HTML 文件并通过向其中插入一些 JSON 来修改它,那么您不能仅仅res.sendFile()
因为它只是从磁盘或缓存读取文件并直接将其作为响应流式传输,提供没有机会修改它。
The more common way of doing this is to use a template system that lets you insert things into an HTML file (usually replacing special tags with your own data). There are literally hundreds of template systems and many that support node.js. Common choices for node.js are Jade (Pug), Handlebars, Ember, Dust, EJS, Mustache.
更常见的方法是使用模板系统,让您将内容插入 HTML 文件(通常用您自己的数据替换特殊标签)。有数以百计的模板系统和许多支持 node.js。node.js 的常见选择是 Jade (Pug)、Handlebars、Ember、Dust、EJS、Mustache。
Or, if you really wanted to do so, you could read the HTML file into memory, use some sort of .replace()
operation on it to insert your own data and then res.send()
the resulting changed file.
或者,如果您真的想这样做,您可以将 HTML 文件读入内存,.replace()
对它使用某种操作来插入您自己的数据,然后res.send()
生成更改后的文件。
回答by Ryan Wheale
I know this is late but I wanted to offer a solution which no one else has provided. This solution allows a file to be streamed to the response while still allowing you to modify the contents without needing a templating engine or buffering the entire file into memory.
我知道这已经晚了,但我想提供一种其他人没有提供的解决方案。此解决方案允许将文件流式传输到响应,同时仍然允许您修改内容而无需模板引擎或将整个文件缓冲到内存中。
Skip to the bottom if you don't care about "why"
如果您不在乎“为什么”,请跳到底部
Let me first describe why res.sendFile
is so desirable for those who don't know. Since Node is single threaded, it works by performing lots and lots of very small tasks in succession - this includes reading from the file system and replying to an http request. At no point in time does Node just stop what it's doing and read an entire from the file system. It will read a little, do something else, read a little more, do something else. The same goes for replying to an http request and most other operations in Node (unless you explicitly use the sync
version of an operation - such as readFileSync - don't do that if you can help it, seriously, don't - it's selfish).
让我首先描述为什么res.sendFile
不知道的人如此受欢迎。由于 Node 是单线程的,它通过连续执行大量非常小的任务来工作 - 这包括从文件系统读取和回复 http 请求。Node 在任何时候都不会停止它正在做的事情并从文件系统中读取整个文件。它会读一点,做别的事情,多读一点,做别的事情。回复 http 请求和 Node 中的大多数其他操作也是如此(除非您明确使用sync
操作的版本 - 例如 readFileSync - 如果你能帮到它,就不要这样做,说真的,不要 - 这是自私的) .
Consider a scenario where 10 users make a request for for the same file. The inefficient thing to do would be to load the entire file into memory and then send the file using res.send()
. Even though it's the same file, the file would be loaded into memory 10 separate times before being sent to the browser. The garbage collector would then need to clean up this mess after each request. The code would be innocently written like this:
考虑一个场景,其中 10 个用户请求同一个文件。低效的做法是将整个文件加载到内存中,然后使用res.send()
. 即使它是同一个文件,该文件在发送到浏览器之前也会被加载到内存中 10 次。垃圾收集器需要在每次请求后清理这些乱七八糟的东西。代码会被无辜地写成这样:
app.use('/index.html', (req, res) => {
fs.readFile('../public/index.html', (err, data) => {
res.send(data.toString());
});
});
That seems right, and it works, but it's terribly inefficient. Since we know that Node does things in small chunks, the best thing to do would be to send the small chunks of data to the browser as they are being read from the file system. The chunks are never stored in memory and your server can now handle orders of magnitude more traffic. This concept is called streaming, and it's what res.sendFile
does - it streams the file directly to the user from the file system and keeps the memory free for more important things. Here's how it looks if you were to do it manually:
这似乎是正确的,它有效,但效率极低。由于我们知道 Node 以小块的形式做事,因此最好的做法是在从文件系统读取数据时将小块数据发送到浏览器。这些块永远不会存储在内存中,您的服务器现在可以处理数量级的流量。这个概念称为流式传输,它就是这样res.sendFile
做的 - 它将文件从文件系统直接流式传输给用户,并为更重要的事情保留空闲内存。如果您要手动执行此操作,则如下所示:
app.use('/index.html', (req, res) => {
fs.createReadStream('../public/index.html')
.pipe(res);
});
Solution
解决方案
If you would like to continue streaming a file to the user while making slight modifications to it, then this solution is for you. Please note, this is not a replacement for a templating engine but should rather be used to make small changes to a file as it is being streamed. The code below will append a small script tag with data to the body of an HTML page. It also shows how to prepend or append content to an http response stream:
如果您想在对文件进行轻微修改的同时继续向用户流式传输文件,那么此解决方案适合您。请注意,这不是模板引擎的替代品,而是应该用于在流式传输文件时对文件进行小的更改。下面的代码将一个带有数据的小脚本标签附加到 HTML 页面的正文中。它还展示了如何在 http 响应流中添加或附加内容:
NOTE: as mentioned in the comments, the original solution could have an edge case where this would fail. For fix this, I have added the new-linepackage to ensure data chunks are emitted at new lines.
注意:如评论中所述,原始解决方案可能会出现失败的边缘情况。为了解决这个问题,我添加了新行包以确保数据块在新行发出。
const Transform = require('stream').Transform;
const parser = new Transform();
const newLineStream = require('new-line');
parser._transform = function(data, encoding, done) {
const str = data.toString().replace('</body>', '<script>var data = {"foo": "bar"};</script></body>');
this.push(str);
done();
};
// app creation code removed for brevity
app.use('/index.html', (req, res) => {
res.write('<!-- Begin stream -->\n');
fs
.createReadStream('../public/index.html')
.pipe(newLineStream())
.pipe(parser)
.on('end', () => {
res.write('\n<!-- End stream -->')
}).pipe(res);
});
回答by user6476673
Well, it's kinda old, but I didn't see any sufficient answer, except for "why not". You DO have way to pass parameters IN static file. And that's quite easy. Consider following code on your origin (using express):
嗯,它有点旧,但我没有看到任何足够的答案,除了“为什么不”。您确实可以在静态文件中传递参数。这很容易。考虑在您的原点上使用以下代码(使用 express):
let data = fs.readFileSync('yourPage.html');
if(data)
res.send(data.replace('param1Place','uniqueData'));
//else - 404
Now for example, just set a cookie, in yourPage.html, something like:
现在,例如,只需在yourPage.html 中设置一个 cookie,例如:
<script>
var date = new Date();
document.cookie = "yourCookieName='param1Place';" +
date.setTime(date.getTime() + 3600) + ";path=/";
</script>
And you can plainly pull content of uniqueDatafrom yourCookieName wherever you want in your js
您可以在 js 中的任何位置从 yourCookieName中提取uniqueData 的内容
回答by Brad
Why not just read the file, apply transformations and then set up the route in the callback?
为什么不直接读取文件,应用转换,然后在回调中设置路由?
fs.readFile(appPath, (err, html) => {
let htmlPlusData = html.toString().replace("DATA", JSON.stringify(data));
app.get('/', (req, res) => {
res.send(htmlPlusData);
});
});
Note that you can't dynamically change data
, you'd have to restart the node instance.
请注意,您不能动态更改data
,您必须重新启动节点实例。
回答by Robert Moskal
You only have one response you can return from the server. The most common thing to do would be to template your file on the server with nunjucks or jade. Another choice is to render the file on the client and then to use javascript to make an ajax call to the server to get additional data. I suppose you could also set some data in a cookie and then read that on the client side via javascript as well.
您只能从服务器返回一个响应。最常见的做法是在服务器上用修女或玉石模板化您的文件。另一种选择是在客户端呈现文件,然后使用 javascript 对服务器进行 ajax 调用以获取其他数据。我想您还可以在 cookie 中设置一些数据,然后也可以通过 javascript 在客户端读取该数据。
回答by Jonah Williams
(Unless you want to template the html file to insert the json data into a script tag). You'll need to expose an api endpoint in express the send along the data to the page, and have a function on the page to access it. for example,
(除非您想对 html 文件进行模板化以将 json 数据插入到脚本标记中)。您需要公开一个 api 端点以将数据发送到页面,并在页面上有一个函数来访问它。例如,
// send the html
app.get('/', (req, res) => res.sendFile('index'));
// send json data
app.get('/data', (req, res) => res.json(data));
Now on the client side you can create a request to access this endpoint
现在在客户端,您可以创建访问此端点的请求
function get() {
return new Promise((resolve, reject) => {
var req = new XMLHttpRequest();
req.open('GET', '/data');
req.onload = () => resolve(req.response);
});
}
// then to get the data, call the function
get().then((data) => {
var parsed = JSON.parse(data);
// do something with the data
});
EDIT:
编辑:
So arrow functions probably don't work client side yet. make sure to replace them with function(){} in your real code
所以箭头函数可能还不能在客户端工作。确保在你的真实代码中用 function(){} 替换它们