Using the advice from this previous post I was able to figure out how to upload a single file from my shiny app to a specific folder on my google drive. This worked perfectly. Unfortunately, I get the following error when I try to select and upload multiple files though. Any help is appreciated!
"Warning in if (!file.exists(media)) { :
the condition has length > 1 and only the first element will be used
Warning: Error in rationalize_path_name: is_string(name) is not TRUE
[No stack trace available]"
Here is minimally reproducible example below
library(googledrive)
ui <- fluidPage(
fileInput(inputId = "file",
label = "Choose file to upload",
accept = NULL,
multiple = TRUE)
)
server <- function(input, output) {
observeEvent(input$file, {
drive_upload(media = input$file$datapath,
name = input$file$name,
path = "my_folder")
})
}
shinyApp(ui, server)
To upload several files you need to iterate on each input$file$datapath, input$file$name pair:
observeEvent(input$file, {
mapply( function(datapath, name){
drive_upload(media = datapath,
name = name,
path = "my_folder")},
input$file$datapath,
input$file$name)
})
Related
The user of my shiny app just created a binary file stored on the shiny server.
It is not a text file nor a zip but a bioinformatics data file (bam).
The file is at a known path inside the shiny app tree => Uploads/data_filtered.bam
I want to let the user download it with a Download button.
How can I modify a downloadHandler block to copy the file to the local client?
I do not find any solution so far and do not want to wrap the bam into a zip to spare the user time decompressing it after download.
Thanks for any piece of code that would do the job
You can use addResourcePath to have shiny serve your file
library(shiny)
ui <- fluidPage(htmlOutput("link"))
server <- function(input, output, session) {
addResourcePath("res", "Uploads")
output$link = renderUI(HTML('Download'))
}
shinyApp(ui = ui, server = server)
If for some reason you cannot use the built-in shiny::downloadHandler() (I did'nt have that luxury), you can always construct your own as shown below. The file to download is located under ./www/. The file is read as binary, sent across the websocket to the front-end, where the binary 'stream' is decoded in JS, and wrapped in a Blob before being downloaded.
library(shiny)
runApp(
list(ui = fluidPage(
tags$head(
tags$script(
"
// handle binary data stream
function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i);
return buf;
}
// download handler
function downloadFile(contentURL, fileName) {
var element = document.createElement('a');
element.setAttribute('href', contentURL);
element.setAttribute('download', fileName);
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
// download file on actionButton click
$(document).ready(function() {
Shiny.addCustomMessageHandler(
'send_data_to_client',
function(d) {
var blobUrl = window.URL.createObjectURL(new Blob([s2ab(atob(d.bin))], {type: \"application/octet-stream\"}));
downloadFile(blobUrl, d.name)
});
});
")
),
titlePanel("sendBinaryMessage example"),
fluidRow(
column(4, wellPanel(
actionButton("controller", "Test"),
))
)
),
server = function(input, output, session){
observeEvent(
input$controller,
{
file_path <- "./www/test.xlsx"
data <- list(
name = "test.xlsx",
bin = readBin(file_path, what=raw(), n=file.info(file_path)$size)
)
session$sendCustomMessage(type = "send_data_to_client", message = data)
}
)
})
)
I found the right function (file.copy) and after some trial and error figured out how to use it
# get info from input file uploaded by the user
BamFile <- reactive({
file <- input$BAM
req(file)
fpath <- file$datapath
fext <- tools::file_ext(fpath)
validate(need(fext %in% c("bam", "BAM"), "Upload must be a .bam or .BAM file"))
res <-
list(
upath = file$datapath,
fname = basename(file$name),
fext = fext
)
return(res)
})
....
# user wants to download the filtered bam file created by the shiny app
# the file was created in uploads='Uploads/' under the name 'data_filtered.bam'
output$downloadBam <- downloadHandler(
filename = function() {
gsub(".bam", "_filtered.bam", BamFile()$fname)
},
content = function(file) {
serverfile <- paste0(uploads,"/data_filtered.bam")
file.copy(serverfile, file)
}
)
I have a code that uploads a file. After uploading the file using a action button "Save to Database", I store the file name and file path in vectors.
In the same app, I have another tab that displays the excel output in form of table. So, to read the file I use the file path retrieved while saving the file using the action button.
The problem is I get "File does not exist" since the path is something like below
"C:\Users\Arun\AppData\Local\Temp\RtmpINivvL/69ff834f0b2623ef2ec95c41/0.xlsx"
While the location I uploaded the file from is
" "D:/Data_Dump/summary.xlsx"
How to solve this issue?
UI.R code
tabItem(tabName = "file",
mainPanel(
titlePanel(h2("Upload your XLSX file here ")), fluidRow(
column(6,
fileInput('file1', 'Choose a XLSX file to upload',
accept = c('.xlsx'))),
column(6,actionButton("save","Save to Database")),
div(DT::dataTableOutput("contents"),style = "font-size: 100%;width: 150%")
)
)
)
server.R code
eventReactive(input$save,{
filenm <- input$file1
filenm$name
tablelist <<- c(tablelist,as.character(filenm$name))
print(tablelist)
filePath <<- c(filePath,as.character(filenm$datapath))
print(filePath)
return (tablelist)
})
one workaround for getting the actual filepath, might be using the shinyFiles package. But you have to ensure the upload functionality by something like file.copy() manually. In addition in shinyFiles you cannot specify a certain filetype to be accepted.
Anyway here is a small example of shinyFiles:
library(shiny)
library(shinyFiles)
ui <- fluidPage(
shinyFilesButton("Btn_GetFile", "Choose a file" ,
title = "Please select a file:", multiple = FALSE,
buttonType = "default", class = NULL),
textOutput("txt_file")
)
server <- function(input,output,session){
tablelist<-NULL
filePath<<-NULL
volumes = getVolumes()
observe({
shinyFileChoose(input, "Btn_GetFile", roots = volumes, session = session)
if(!is.null(input$Btn_GetFile)){
browser()
file_selected<-parseFilePaths(volumes, input$Btn_GetFile)
#check if file extension is .xlsx
tablelist <<- c(tablelist,as.character(file_selected$name))
print(tablelist)
filePath <<- c(filePath,as.character(file_selected$datapath))
print(filePath)
#upload file via e.g. file.copy()
output$txt_file <- renderText(as.character(file_selected$datapath))
}
})
}
shinyApp(ui = ui, server = server)
I am almost done creating a survey using Rshiny. I want to create a button that will allow the user to upload an image (jpeg,tiff,png etc) to the R shiny server, Dropbox, or google drive. It seems the fileInput method only accepts documents like csv? any help would be appreciated!
fileInput actually allows the import of any type of files. You just need to set the argument accept to NULL or to any file extension that you accept. What you need to understand first, is that if you use fileInput, it will actually upload your file to the tmp folder (the path to the uploaded data is stored in input$file$datapath), and only then you will be able to upload your file to googledrive or to whatever cloud. A solution using fileInput could look like this:
library(googledrive)
ui <- fluidPage(
fileInput(inputId = "file",
label = "Choose file to upload",
accept = NULL)
)
server <- function(input, output) {
observeEvent(input$file, {
drive_upload(media = input$file$datapath,
name = input$file$name)
})
}
shinyApp(ui, server)
If the "double upload" is a problem for you, you can avoid this by using the package shinyFiles. A nice answer has been posted here and here's a way to adapt the code to address your specific problem.
library(googledrive)
library(shinyFiles)
ui <- fluidPage(
shinyFilesButton("Btn_GetFile", "Choose file to upload" ,
title = "Please select a file:", multiple = FALSE,
buttonType = "default", class = NULL)
)
server <- function(input, output, session) {
volumes = getVolumes()
observe({
shinyFileChoose(input, "Btn_GetFile", roots = volumes, session = session)
if (!is.null(input$Btn_GetFile)){
file_selected <- parseFilePaths(volumes, input$Btn_GetFile)
drive_upload(media = as.character(file_selected$datapath),
name = as.character(file_selected$name))
}
})
}
shinyApp(ui = ui, server = server)
Hi all this is my first question, so I hope the format is correct.
I'm looking to create a R shiny app that calls on all the .txt files that that are within the parameters set by the user, merges those .txt files, and produces a word cloud.
The app located here https://lukaszsowinski.shinyapps.io/WC-app/ crashes once it is opened, although it works on my local computer. I'm almost sure it has to do with the way I'm writing in my wd(), data files, and .txt files as they were giving me problems before I was able to finally published. This is the error I'm getting
2017-08-12T01:17:22.742107+00:00 shinyapps[204302]: Error in loadWorkbook(file) :
2017-08-12T01:17:22.742109+00:00 shinyapps[204302]: Cannot find /home/shiny/WC-app/WordMappingInfo.xlsx
Here is the ines of code that I'm not sure where my error is.
library(rsconnect)
rsconnect::deployApp('C:/Users/Lukasz Sowinski/Desktop/WC-app')
server <- function(input, output, server)
{
textframe <- read.xlsx("C:/Users/Lukasz Sowinski/Desktop/WC-app/WordMappingInfo.xlsx", 1)
schooldata <- list (
"Num" = textframe$Num[!is.na(textframe$Num)],
"SchoolName" = C(textframe$SchoolName),
"Path" = c(
"C:/Users/Lukasz Sowinski/Desktop/WC-app/texts/Queens College.txt",
"C:/Users/Lukasz Sowinski/Desktop/WC-app/texts/Lehman College.txt",
"C:/Users/Lukasz Sowinski/Desktop/WC-app/texts/Denison University.txt",
"C:/Users/Lukasz Sowinski/Desktop/WC-app/texts/St Johns University.txt",
"C:/Users/Lukasz Sowinski/Desktop/WC-app/texts/Rutgers University.txt"
),
"Students" = textframe$Students[!is.na(textframe$Students)],
"Tuition" = textframe$Tuition[!is.na(textframe$Tuition)],
"Program" = textframe$Program[!is.na(textframe$Program)]
)
Here is the some of the cod where I call up the data as needed based on user input.
if (schooldata$Program[i] == input$selection)
{
if (temptext == 0)
{
temptext <- schooldata$Path[i]
temptext.1 <- readLines(temptext)
mastertext.1 = temptext.1
}
temptext <- schooldata$Path[i]
temptext.1 <- readLines(temptext)
mastertext.1 <- rbind(mastertext.1, temptext.1)
}
I've also tried using pathways
"./WC-app/WordMappingInfo.xlsx"
"~/WC-app/WordMappingInfo.xlsx"
That I found in similar questions but neither work.
Any help would be tremendously appreciated. Thank you
Have you tried with shinyFiles CRAN packages extension?
In the ui.R file
shinyUI(bootstrapPage(
shinyFilesButton('files', label='File select', title='Please select a file', multiple=FALSE)
))
In the server.R file
shinyServer(function(input, output) {
shinyFileChoose(input, 'files', root=c(root='.'), filetypes=c('', 'txt'))
})
I am building a shiny app that requires a user to select a folder on the local machine, which contains the files to be processed by the app.
I am using a solution proposed here. This works fine on a local machine, but does not work if the app is deployed to a shinyapps server.
The author of this solution confirmed that it was only designed to work with local Shiny apps, since it makes OS shell calls to display a directory dialog.
I am wondering if there is a different solution for directory dialog, which will work on the deployed Shiny apps (I am deploying to shinyapps.io).
Edited: Notice that I cannot use fileInput interface for two reasons:
The users of the app are not technical people and they do not know which files inside the folder are used by the app.
The selected folder may contain other folders within which the needed files reside, such that it is impossible to select all files at once, even if the fileInput interface has the multiple option enabled.
The folder/files structure is not something I can change, it is downloaded AS IS from a medical device and therefore the only thing I can expect from the users is to specify the parent folder and the rest should be done inside the R code.
This is a working example based on using the "webkitdirectory" attribute. At the moment the attribute is supported by Chrome, Opera and Safari (mobile and desktop) and it should be supported in Firefox 49 to be released in September.
More about this here. It work with subdirectories also.
It requires using the tags keyword in ui.R. I have tested it by uploading three csv files each contaning three numbers separeted by a coma. Tested locally and on shinyapps.io with Chrome and Opera. This is the code:
ui.R
library(shiny)
library(DT)
shinyUI(tagList(fluidPage(theme = "bootstrap.css",
includeScript("./www/text.js"),
titlePanel("Folder content upload"),
fluidRow(
column(4,
wellPanel(
tags$div(class="form-group shiny-input-container",
tags$div(tags$label("File input")),
tags$div(tags$label("Choose folder", class="btn btn-primary",
tags$input(id = "fileIn", webkitdirectory = TRUE, type = "file", style="display: none;", onchange="pressed()"))),
tags$label("No folder choosen", id = "noFile"),
tags$div(id="fileIn_progress", class="progress progress-striped active shiny-file-input-progress",
tags$div(class="progress-bar")
)
),
verbatimTextOutput("results")
)
),
column(8,
tabsetPanel(
tabPanel("Files table", dataTableOutput("tbl")),
tabPanel("Files list", dataTableOutput("tbl2"))
)
)
)
),
HTML("<script type='text/javascript' src='getFolders.js'></script>")
)
)
server.R
library(shiny)
library(ggplot2)
library(DT)
shinyServer(function(input, output, session) {
df <- reactive({
inFiles <- input$fileIn
df <- data.frame()
if (is.null(inFiles))
return(NULL)
for (i in seq_along(inFiles$datapath)) {
tmp <- read.csv(inFiles$datapath[i], header = FALSE)
df <- rbind(df, tmp)
}
df
})
output$tbl <- DT::renderDataTable(
df()
)
output$tbl2 <- DT::renderDataTable(
input$fileIn
)
output$results = renderPrint({
input$mydata
})
})
text.js
window.pressed = function(){
var a = document.getElementById('fileIn');
if(a.value === "")
{
noFile.innerHTML = "No folder choosen";
}
else
{
noFile.innerHTML = "";
}
};
getFolders.js
document.getElementById("fileIn").addEventListener("change", function(e) {
let files = e.target.files;
var arr = new Array(files.length*2);
for (let i=0; i<files.length; i++) {
//console.log(files[i].webkitRelativePath);
//console.log(files[i].name);
arr[i] = files[i].webkitRelativePath;
arr[i+files.length] = files[i].name;
}
Shiny.onInputChange("mydata", arr);
});
Let me know if this helps.
Have you tried around with the shinyFiles package?
There is a widget which lets you chose a directory.
As output you get the path of that directory which you can in turn use to access the files.
Here is an example how it works.
server
library(shiny)
library(shinyFiles)
shinyServer(function(input, output, session) {
# dir
shinyDirChoose(input, 'dir', roots = c(home = '~'), filetypes = c('', 'txt'))
dir <- reactive(input$dir)
output$dir <- renderPrint(dir())
# path
path <- reactive({
home <- normalizePath("~")
file.path(home, paste(unlist(dir()$path[-1]), collapse = .Platform$file.sep))
})
# files
output$files <- renderPrint(list.files(path()))
})
ui
library(shiny)
library(shinyFiles)
shinyUI(fluidPage(sidebarLayout(
sidebarPanel(
shinyDirButton("dir", "Chose directory", "Upload")
),
mainPanel(
h4("output$dir"),
verbatimTextOutput("dir"), br(),
h4("Files in that dir"),
verbatimTextOutput("files")
)
)))
Hope this helps.