我有以下代码段:
write.csv(mtcars, 'mtcars.csv')
create_issue_spotter_data = function(df) {
df$new_column = 0
df = df[df$mpg < 20, ]
return(df)
}
ui <- fluidPage(
fluidRow(align = "center", downloadButton("download_btn")),
fluidRow(align = "center", plotOutput("distPlot"))
)
server <- function(input, output) {
start <- 1
file <- reactiveFileReader(
intervalMillis = 1000 * 60 * 5,
session = NULL,
filePath = 'mtcars.csv',
readFunc = readr::read_csv
)
this_df = eventReactive(file(), {
if (start != 1) {
print(paste("Now updating the data. Date:", Sys.time()))
saved_df <<- create_issue_spotter_data(file())
df <- saved_df
} else {
start <<- start + 1
print(paste("Everything's good at", Sys.time()))
df <- saved_df
}
df
})
output$download_btn <- downloadHandler(
filename = paste0(paste("Issue", "Spotter", gsub("-", "_", Sys.Date()), sep = "_"), ".pdf"),
content = function(file) {
pdf(file)
p = build_cluster_viz(this_df(), download = TRUE)
print(p)
dev.off()
}, contentType = "pdf")
output$distPlot <- renderPlot({
plot(this_df()$mpg, this_df()$disp)
})
}
我评论为“从服务器内存中发送文件的聪明方法”的代码来自这篇文章: Node Express.js - Download file from memory - 'filename must be a string'
这是从内存中获取一个字符串,并将其作为.txt文件提供给客户端。
此代码曾经有效。
然后我决定添加 app.post('/pst', function(req, res) {
var data = req.body.convo;
res.render('waiting.ejs'); //ADDED THIS
myFunc(data).then(result => {
res.render('success.ejs'); //THEN THIS
//---------------------------------
//clever way to send text file to client from the memory of the server
var fileContents = Buffer.from(result, 'ascii');
var readStream = new stream.PassThrough();
readStream.end(fileContents);
res.set('Content-disposition', 'attachment; filename=' + fileName);
res.set('Content-Type', 'text/plain');
readStream.pipe(res);
//--------------------------------------
}).catch( .....
行,但出现此错误:
res.render('waiting.ejs');
然后我尝试在代码将.txt文件发送到客户端之前和之后添加另一个res.render()[在这种情况下为Error: Can't set headers after they are sent.
]。
错误仍然存在。另外,也没有重定向到success.ejs,换句话说,res.render('success.ejs');
从未工作过,尽管它是否放在那段代码之后。
答案 0 :(得分:1)
您将必须检查express.js源代码(here):
res.render = function render(view, options, callback) {
var app = this.req.app;
var done = callback;
var opts = options || {};
var req = this.req;
var self = this;
// support callback function as second arg
if (typeof options === 'function') {
done = options;
opts = {};
}
// merge res.locals
opts._locals = self.locals;
// default callback to respond
done = done || function (err, str) {
if (err) return req.next(err);
self.send(str);
};
// render
app.render(view, opts, done);
};
您可以看到,当您使用res.render()
方法时,它将完成的回调传递给app.render(...)
(source code),然后它将done
传递给{{1 }}等
最后,如果成功,它将用tryInitView
调用done
回调,如果失败则用str
调用回调。然后,它会在err
回调中触发res.send()
,这只会阻止您在此之后设置标头。
答案 1 :(得分:1)
app.post('/pst', function(req, res) {
var data = req.body.convo;
myFunc(data).then(result => {
//---------------------------------
//clever way to send text file to client from the memory of the server
var fileContents = Buffer.from(result, 'ascii');
var readStream = new stream.PassThrough();
readStream.end(fileContents);
res.set('Content-disposition', 'attachment; filename=' + fileName);
res.set('Content-Type', 'text/plain');
readStream.pipe(res);
res.redirect(`/success`); //THEN THIS
//--------------------------------------
}).catch( .....
当您使用app.use方法添加中间件来表达(基于connect建立)时,您将在connect中将项目附加到Server.prototype.stack
上。
服务器收到请求后,将在堆栈上进行迭代,并调用(request,response,next)方法。
问题是,如果在一个中间件项目中写入响应主体或标头(看起来是由于某种原因,要么是由于某种原因),但没有调用response.end()
,而您调用了{{1 }},然后在核心Server.prototype.handle方法完成后,就会注意到:
next()
因此,它将引发错误。但是它引发的错误只是这个基本响应(来自连接http.js源代码:
there are no more items in the stack, and/or
that response.headerSent is true.
有问题的中间件在不调用res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end('Cannot ' + req.method + ' ' + req.url);
的情况下设置了响应标头,并调用了next(),这使express的服务器感到困惑。
因此您可以通过response.end()
设置标题。现在,如果您尝试再次渲染,则会引发错误。
res.render()
答案 2 :(得分:1)
res.render()函数编译您的模板,在其中插入本地语言,并从这两件事中创建html输出。这就是为什么会出现错误。 不要两次使用它,因为它发送响应。