闪亮的应用程序下载按钮和 future_promise:enc2utf8 错误:参数不是字符向量
shiny app download button and future_promise: Error in enc2utf8: argument is not a character vector
我正在尝试在 shiny 中创建一个下载处理程序,但使用 future_promise() 因为写入文件可能需要一些时间。这是我想做的工作示例,但不使用异步框架:
一个有效的 .Rmd 闪亮应用程序:当您单击该按钮时,它会将 10 个随机偏差写入一个文件并提供下载。我添加了 5 秒的延迟。
---
title: "download, no futures"
runtime: shiny
output: html_document
---
```{r setup, include=FALSE}
library(dplyr)
knitr::opts_chunk$set(echo = FALSE)
```
This version works.
```{r}
renderUI({
button_reactive <- reactive({
y = rnorm(10)
Sys.sleep(5)
tf = tempfile(fileext = ".txt")
cat(c(y,'\n'), sep='\n', file = tf)
d = readBin(con = tf, what = "raw", n = file.size(tf))
return(list(fn = basename(tf), d = d))
})
output$button <- downloadHandler(
filename = function() {
button_reactive() %>%
`[[`('fn')
},
content = function(f) {
d = button_reactive() %>%
`[[`('d')
con = file(description = f, open = "wb")
writeBin(object = d, con = con)
close(con)
}
)
shiny::downloadButton(outputId = "button", label="Download")
})
我正在尝试使用 future_promise 在异步框架中实现它。这是{future}/{promises} 版本:
---
title: "download futures"
runtime: shiny
output: html_document
---
```{r setup, include=FALSE}
library(future)
library(promises)
plan(multisession)
library(dplyr)
knitr::opts_chunk$set(echo = FALSE)
```
This version yields this error on download attempt, reported in the R console:
```
Warning: Error in enc2utf8: argument is not a character vector
[No stack trace available]
```
```{r}
renderUI({
button_reactive <- reactive({
future_promise({
y = rnorm(10)
Sys.sleep(5)
tf = tempfile(fileext = ".txt")
cat(c(y,'\n'), sep='\n', file = tf)
d = readBin(con = tf, what = "raw", n = file.size(tf))
return(list(fn = basename(tf), d = d))
}, seed = TRUE)
})
output$button <- downloadHandler(
filename = function() {
button_reactive() %...>%
`[[`('fn')
},
content = function(f) {
con = file(description = f, open = "wb")
d = button_reactive() %...>%
`[[`('d') %...>%
writeBin(object = ., con = con)
close(con)
}
)
shiny::downloadButton(outputId = "button", label="Download")
})
当我在 Firefox 中单击按钮时,我没有得到任何文件,在 R 控制台中,显示如下:
Warning: Error in enc2utf8: argument is not a character vector
[No stack trace available]
经过一些调试,我相信这是因为 运行 下载处理程序是 运行 filename
函数,需要一个字符向量,并得到一个承诺。但我不确定如何解决这个问题。
我看到这个question,提问者似乎有同样的问题,但没有提供解决方案(他们的例子不可重现)。
我该如何解决这个问题?
Promises 与 R Markdown 一起使用,但也有好消息和坏消息。
好消息
承诺在 downloadHandler 上工作
简而言之,可以使用 promises 代替 return 值:它只是在稍后某个时间点提供的输出值。因此,对于任何输出对象,包括 downloadHandler
,您可以提供承诺而不是输出值。
一个 promise 由一个 future_promise()
函数组成,它执行一些缓慢的 运行 操作(通常在不同的 R 会话中)和一个解决部分(这是 [= 后面的部分) 15=] operator) 获取结果并提供解决方案。两者的组合就是 promise
.
downloadHandler
有点特殊,因为它不接收对象作为输出,但希望将名称为 f
的文件写入磁盘(因此 NULL
return 值)。您的原始代码 return 编辑了 close(con)
,这阻碍了代码的工作(但不是错误的原因)。
要在 downloadHandler
上使用 promise,必须将文件写入磁盘替换为 promise。但是,在您的代码中,您的最后一行是 close(con)
,这不是一个承诺。因此,首要任务是将文件写入函数卸载,然后它可以成为未来构造的解析部分。
downloadHandler
似乎不支持 filename
部分的承诺,如@Waldi 所述。我没有这方面的支持信息。
坏消息
Promise 在 R markdown 上下文中没有多大意义
如 in this article 所述,可以在 Shiny 上下文中使用 promises,并防止服务器 跨会话 锁定。在单个会话中,事件循环等待所有承诺在呈现输出之前解决,有效地导致我们都学会喜欢的完全相同的卡住 UI。只有当第二个会话处于活动状态时,承诺才会产生任何性能优势。
使用 promises 的 downloadHandler 的完整示例
下面的代码是你上面代码的改编,有三个小的区别:
- 各种 future 和 resolve 函数已经隔离
downloadHandler
filename
参数现在是静态的
downloadHandler
content
参数提供了完整的承诺
保留序言
---
title: "download futures"
runtime: shiny
output: html_document
---
```{r setup, include=FALSE}
library(future)
library(promises)
plan(multisession)
library(dplyr)
knitr::opts_chunk$set(echo = FALSE)
```
为了更清晰,定义两个独立函数。请注意 writeFile
处理所有 I/O ,包括关闭连接
```{r}
createFile = function(){
y = rnorm(10)
Sys.sleep(1)
tf = tempfile(fileext = ".txt")
cat(c(y,'\n'), sep='\n', file = tf)
d = readBin(con = tf, what = "raw", n = file.size(tf))
return(list(fn = basename(tf), d = d))
}
writeFile = function (fut, f){
x = fut[['d']]
con = file(description = f, open = "wb")
writeBin(object = x, con = con)
close(con)
}
```
UI 部分:注意内容现在 return 是一个承诺。
```{r}
renderUI({
testPromise = reactive({
future_promise({createFile()}, seed=T) %...>% (function (x) (x))()
})
fileName = reactive({
testPromise() %...>% '[['('fn')
})
output$button <- downloadHandler(
filename = function() {
'test.txt'
# This doesn't work - filename apparently doesn't support promises
# fileName()
},
content = function(f) {
# Content needs to receive promise as return value, so including resolution
testPromise() %...>% writeFile(., f)
}
)
shiny::downloadButton(outputId = "button", label="Download")
})
```
我正在尝试在 shiny 中创建一个下载处理程序,但使用 future_promise() 因为写入文件可能需要一些时间。这是我想做的工作示例,但不使用异步框架:
一个有效的 .Rmd 闪亮应用程序:当您单击该按钮时,它会将 10 个随机偏差写入一个文件并提供下载。我添加了 5 秒的延迟。
---
title: "download, no futures"
runtime: shiny
output: html_document
---
```{r setup, include=FALSE}
library(dplyr)
knitr::opts_chunk$set(echo = FALSE)
```
This version works.
```{r}
renderUI({
button_reactive <- reactive({
y = rnorm(10)
Sys.sleep(5)
tf = tempfile(fileext = ".txt")
cat(c(y,'\n'), sep='\n', file = tf)
d = readBin(con = tf, what = "raw", n = file.size(tf))
return(list(fn = basename(tf), d = d))
})
output$button <- downloadHandler(
filename = function() {
button_reactive() %>%
`[[`('fn')
},
content = function(f) {
d = button_reactive() %>%
`[[`('d')
con = file(description = f, open = "wb")
writeBin(object = d, con = con)
close(con)
}
)
shiny::downloadButton(outputId = "button", label="Download")
})
我正在尝试使用 future_promise 在异步框架中实现它。这是{future}/{promises} 版本:
---
title: "download futures"
runtime: shiny
output: html_document
---
```{r setup, include=FALSE}
library(future)
library(promises)
plan(multisession)
library(dplyr)
knitr::opts_chunk$set(echo = FALSE)
```
This version yields this error on download attempt, reported in the R console:
```
Warning: Error in enc2utf8: argument is not a character vector
[No stack trace available]
```
```{r}
renderUI({
button_reactive <- reactive({
future_promise({
y = rnorm(10)
Sys.sleep(5)
tf = tempfile(fileext = ".txt")
cat(c(y,'\n'), sep='\n', file = tf)
d = readBin(con = tf, what = "raw", n = file.size(tf))
return(list(fn = basename(tf), d = d))
}, seed = TRUE)
})
output$button <- downloadHandler(
filename = function() {
button_reactive() %...>%
`[[`('fn')
},
content = function(f) {
con = file(description = f, open = "wb")
d = button_reactive() %...>%
`[[`('d') %...>%
writeBin(object = ., con = con)
close(con)
}
)
shiny::downloadButton(outputId = "button", label="Download")
})
当我在 Firefox 中单击按钮时,我没有得到任何文件,在 R 控制台中,显示如下:
Warning: Error in enc2utf8: argument is not a character vector
[No stack trace available]
经过一些调试,我相信这是因为 运行 下载处理程序是 运行 filename
函数,需要一个字符向量,并得到一个承诺。但我不确定如何解决这个问题。
我看到这个question,提问者似乎有同样的问题,但没有提供解决方案(他们的例子不可重现)。
我该如何解决这个问题?
Promises 与 R Markdown 一起使用,但也有好消息和坏消息。
好消息
承诺在 downloadHandler 上工作
简而言之,可以使用 promises 代替 return 值:它只是在稍后某个时间点提供的输出值。因此,对于任何输出对象,包括 downloadHandler
,您可以提供承诺而不是输出值。
一个 promise 由一个 future_promise()
函数组成,它执行一些缓慢的 运行 操作(通常在不同的 R 会话中)和一个解决部分(这是 [= 后面的部分) 15=] operator) 获取结果并提供解决方案。两者的组合就是 promise
.
downloadHandler
有点特殊,因为它不接收对象作为输出,但希望将名称为 f
的文件写入磁盘(因此 NULL
return 值)。您的原始代码 return 编辑了 close(con)
,这阻碍了代码的工作(但不是错误的原因)。
要在 downloadHandler
上使用 promise,必须将文件写入磁盘替换为 promise。但是,在您的代码中,您的最后一行是 close(con)
,这不是一个承诺。因此,首要任务是将文件写入函数卸载,然后它可以成为未来构造的解析部分。
downloadHandler
似乎不支持 filename
部分的承诺,如@Waldi 所述。我没有这方面的支持信息。
坏消息
Promise 在 R markdown 上下文中没有多大意义
如 in this article 所述,可以在 Shiny 上下文中使用 promises,并防止服务器 跨会话 锁定。在单个会话中,事件循环等待所有承诺在呈现输出之前解决,有效地导致我们都学会喜欢的完全相同的卡住 UI。只有当第二个会话处于活动状态时,承诺才会产生任何性能优势。
使用 promises 的 downloadHandler 的完整示例
下面的代码是你上面代码的改编,有三个小的区别:
- 各种 future 和 resolve 函数已经隔离
downloadHandler
filename
参数现在是静态的downloadHandler
content
参数提供了完整的承诺
保留序言
---
title: "download futures"
runtime: shiny
output: html_document
---
```{r setup, include=FALSE}
library(future)
library(promises)
plan(multisession)
library(dplyr)
knitr::opts_chunk$set(echo = FALSE)
```
为了更清晰,定义两个独立函数。请注意 writeFile
处理所有 I/O ,包括关闭连接
```{r}
createFile = function(){
y = rnorm(10)
Sys.sleep(1)
tf = tempfile(fileext = ".txt")
cat(c(y,'\n'), sep='\n', file = tf)
d = readBin(con = tf, what = "raw", n = file.size(tf))
return(list(fn = basename(tf), d = d))
}
writeFile = function (fut, f){
x = fut[['d']]
con = file(description = f, open = "wb")
writeBin(object = x, con = con)
close(con)
}
```
UI 部分:注意内容现在 return 是一个承诺。
```{r}
renderUI({
testPromise = reactive({
future_promise({createFile()}, seed=T) %...>% (function (x) (x))()
})
fileName = reactive({
testPromise() %...>% '[['('fn')
})
output$button <- downloadHandler(
filename = function() {
'test.txt'
# This doesn't work - filename apparently doesn't support promises
# fileName()
},
content = function(f) {
# Content needs to receive promise as return value, so including resolution
testPromise() %...>% writeFile(., f)
}
)
shiny::downloadButton(outputId = "button", label="Download")
})
```