R Shiny应用误判内容相同文件名不同的CSV为同一文件的问题
问题根源
Shiny 的fileInput组件默认会根据文件内容的哈希值判断响应值是否变化。当上传文件名不同但内容完全一致的文件时,input$file1/input$file2不会被标记为新的响应变化,导致关联的重置逻辑(清空状态、禁用下载按钮)不触发;同时如果之前的处理结果仍保留,会让用户误以为应用未重新执行处理流程。
解决方法
方法1:监听文件名变化(推荐)
修改文件选择的监听逻辑,改为监听文件名而非整个文件对象,确保只要文件名变化就触发重置,同时清空之前的处理结果,强制点击PROCESS按钮时重新执行处理:
# 修改第一个tab的文件选择监听逻辑 observeEvent(input$file1$name, { output$ready1 <- renderText("") shinyjs::removeClass("download1", "button_color") shinyjs::disable("download1") result_f1(NULL) # 重置之前的处理结果 }, ignoreNULL = FALSE) # 修改第二个tab的文件选择监听逻辑 observeEvent(input$file2$name, { output$ready2 <- renderText("") shinyjs::removeClass("download2", "button_color") shinyjs::disable("download2") result_f2(NULL) # 重置之前的处理结果 }, ignoreNULL = FALSE)
方法2:强制重置fileInput组件状态
使用shinyjs的reset函数,强制重置fileInput的状态,确保每次文件选择都被视为新操作:
# 修改第一个tab的文件选择监听逻辑 observeEvent(input$file1, { invalidateLater(100) # 延迟确保文件上传完成 shinyjs::reset("file1") output$ready1 <- renderText("") shinyjs::removeClass("download1", "button_color") shinyjs::disable("download1") result_f1(NULL) }, ignoreNULL = FALSE) # 修改第二个tab的文件选择监听逻辑 observeEvent(input$file2, { invalidateLater(100) shinyjs::reset("file2") output$ready2 <- renderText("") shinyjs::removeClass("download2", "button_color") shinyjs::disable("download2") result_f2(NULL) }, ignoreNULL = FALSE)
完整修改后的代码
library(shiny) library(shinyjs) library(RMySQL) library(tidyverse) library(lubridate) library(rio) ui <- fluidPage( useShinyjs(), tags$head( tags$style( HTML( " .button_color { background-color: #89CFF0; border-color: white; } " ) ) ), tabsetPanel(type = "tabs", tabPanel("FIRST PURCHASE", fileInput("file1", "Choose CSV file ", accept = ".csv"), actionButton("process1", "PROCESS"), downloadButton("download1", "Download Processed CSV", style = "color: grey;"), textOutput("ready1")), tabPanel("COUPON INFO", fileInput("file2", "Choose CSV file ", accept = ".csv"), actionButton("process2", "PROCESS"), downloadButton("download2", "Download Processed CSV", style = "color: grey;"), textOutput("ready2")) ) ) server <- function(input, output, session) { result_f1 <- reactiveVal(NULL) result_f2 <- reactiveVal(NULL) observeEvent(input$process1, { req(input$file1) result1 <- tryCatch({ ### Database Connection & Tables Connection ------------------- api_backend <- dbConnect( MySQL(), dbname = "api_backend", user = "readonly_user", password = "password123", host = "fundednext-production.cqtlpb5sm2vt.ap-northeast-1.rds.amazonaws.com", port = 3306 ) # Input Email list email_list <- read.csv(input$file1$datapath) %>% filter(email != "") %>% mutate(email = str_trim(tolower(email))) # Find customers that are present in the email list customers_df <- tbl(api_backend, "customers") %>% filter(email %in% !!email_list$email) %>% collect() # Find accounts needed (doing this to make the code faster) accounts_df <- tbl(api_backend, "accounts") %>% filter(customer_id %in% !!customers_df$id) %>% collect() # Find only the necessary orders orders_df <- tbl(api_backend, "orders") %>% filter(account_id %in% !!accounts_df$id) %>% collect() coupons_df <- collect(tbl(api_backend, "coupons")) orders_df_first_order <- orders_df %>% filter(status == 1) %>% distinct(customer_id, .keep_all = T) email_list %>% left_join( select(customers_df, email, customer_id = id) ) %>% left_join( select(orders_df_first_order, customer_id, purchase_date = created_at, coupon_id, price = grand_total) ) %>% left_join( select(coupons_df, coupon_id = id, coupon_code = code) ) %>% select(email, affiliate, price, purchase_date, coupon_code) }, error = function(e) { return(paste( "Some error happen!", "Error message:", e, sep = "\n" )) }, finally = { lapply(dbListConnections(MySQL()), dbDisconnect) }) #forcing result to be a data frame result_f1(as.data.frame(result1)) }) #2nd tab observeEvent(input$process2, { req(input$file2) result2 <- tryCatch({ ### Database Connection & Tables Connection ------------------- api_backend <- dbConnect( MySQL(), dbname = "api_backend", user = "readonly_user", password = "password123", host = "fundednext-production.cqtlpb5sm2vt.ap-northeast-1.rds.amazonaws.com", port = 3306 ) # Input Coupon list coupon_list <- read.csv(input$file2$datapath) %>% filter(coupon != "") %>% mutate(coupon = str_trim(coupon)) # Find coupon ids coupons_df <- tbl(api_backend, "coupons") %>% filter(code %in% !!coupon_list$coupon) %>% select(coupon_id=id, coupon = code) %>% collect() # Orders df filter orders_df <- tbl(api_backend, "orders") %>% filter(coupon_id %in% !!coupons_df$coupon_id) %>% collect() # Find customers that are present in the orders customers_df <- tbl(api_backend, "customers") %>% filter(id %in% !!orders_df$customer_id) %>% collect() result_df <- orders_df %>% filter(status == 1) %>% group_by(customer_id) %>% mutate(purchase_number = row_number()) %>% mutate(first_purchase = case_when( purchase_number == 1 ~ "Yes", T ~ "No" )) %>% ungroup() %>% left_join(select(customers_df, customer_id=id, email)) %>% left_join(coupons_df) %>% select(coupon, revenue = grand_total, email, purchase_date = created_at, first_purchase) }, error = function(e) { return(paste( "Some error happen!", "Error message:", e, sep = "\n" )) }, finally = { lapply(dbListConnections(MySQL()), dbDisconnect) }) #forcing result to be a data frame result_f2(as.data.frame(result2)) }) observe({ shinyjs:: disable('download1') }) observe({ shinyjs:: disable('download2') }) # 修改后的第一个tab文件选择监听 observeEvent(input$file1$name, { output$ready1 <- renderText("") shinyjs::removeClass("download1", "button_color") shinyjs::disable("download1") result_f1(NULL) }, ignoreNULL = FALSE) # 修改后的第二个tab文件选择监听 observeEvent(input$file2$name, { output$ready2 <- renderText("") shinyjs::removeClass("download2", "button_color") shinyjs::disable("download2") result_f2(NULL) }, ignoreNULL = FALSE) observe({ shinyjs::toggleClass("download1", "button_color", condition = (class(result_f1()) == "data.frame")) if (class(result_f1()) == "character") { output$ready1 <- renderText({ shinyjs::disable("download1") "There is an issue." }) } else if (class(result_f1()) == "data.frame") { output$ready1 <- renderText({ "The file is ready for download." }) shinyjs::enable("download1") } }) observe({ shinyjs::toggleClass("download2", "button_color", condition = (class(result_f2()) == "data.frame")) if (class(result_f2()) == "character") { output$ready2 <- renderText({ shinyjs::disable("download2") "There is an issue." }) } else if (class(result_f2()) == "data.frame") { output$ready2 <- renderText({ "The file is ready for download." }) shinyjs::enable("download2") } }) output$download1 <- downloadHandler( filename = function() { paste("req_file1", ".csv", sep = "") }, content = function(file) { write.csv(result_f1(), file, row.names = FALSE) } ) output$download2 <- downloadHandler( filename = function() { paste("req_file2", ".csv", sep = "") }, content = function(file) { write.csv(result_f2(), file, row.names = FALSE) } ) } shinyApp(ui = ui, server = server)
内容的提问来源于stack exchange,提问作者RockMachine98
相关产品推荐
相关产品推荐

