R Plumber生成可下载Excel文件失败,请求排查问题
Fix: Plumber POST Endpoint Returning Corrupted Excel File
Let's break down what's causing the garbled response and unopenable Excel file in your code, then fix it step by step:
Key Issues in Your Original Code
- Conflicting Serialization Logic: You’re using both the
@serializer contentTypeannotation andinclude_file()in the same endpoint. This creates a conflict—include_file()already handles setting the correct Content-Type header automatically, while the extra serializer annotation can corrupt the binary Excel data by forcing text-based serialization. - Incorrect HTTP Method: Your requirement specifies a POST endpoint, but you’re using
@get/excel. While this might not directly cause corruption, it will fail if clients try to send POST requests. - Missing Download Header: Without a
Content-Dispositionheader, your browser treats the binary Excel data as plain text (hence the garbled output) and doesn’t prompt for a download. Saved files also lack proper metadata for Excel to recognize the format. - Risk of File Name Collisions: Using a fixed filename in
tempdir()could cause conflicts if multiple requests are processed at the same time.
Corrected Code (Option 1: Using include_file() without extra serializer)
# Load required packages library(plumber) library(xlsx) # Or use openxlsx (pure R, no Java dependency) #* @post /excel # Match your POST requirement function(req, res) { # Create a unique temporary Excel file to avoid collisions temp_excel <- tempfile(fileext = ".xlsx") # Write your data to the temp file (replace iris with your xlsx_df if needed) write.xlsx2(iris, temp_excel, row.names = FALSE) # Tell the browser to download this as a named Excel file res$setHeader( "Content-Disposition", 'attachment; filename="Output_File.xlsx"' ) # Return the file with correct content type include_file( temp_excel, res, contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ) }
Corrected Code (Option 2: Return raw binary data with serializer)
If you prefer to handle the binary read yourself, you can use the serializer annotation without include_file():
library(plumber) library(xlsx) #* @post /excel #* @serializer contentType list(type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") function(req, res) { temp_excel <- tempfile(fileext = ".xlsx") write.xlsx2(iris, temp_excel, row.names = FALSE) res$setHeader( "Content-Disposition", 'attachment; filename="Output_File.xlsx"' ) # Read and return the raw binary content of the file readBin(temp_excel, "raw", file.info(temp_excel)$size) }
Bonus: Use openxlsx Instead of xlsx
The xlsx package relies on Java, which can cause environment issues. For a more stable, pure-R alternative, switch to openxlsx:
library(plumber) library(openxlsx) #* @post /excel function(req, res) { temp_excel <- tempfile(fileext = ".xlsx") write.xlsx(iris, temp_excel, rowNames = FALSE) res$setHeader( "Content-Disposition", 'attachment; filename="Output_File.xlsx"' ) include_file( temp_excel, res, contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ) }
Why This Works
- No more serialization conflicts: We either use
include_file()(with auto Content-Type handling) or the serializer annotation with raw binary data—never both. - The
Content-Dispositionheader triggers a download prompt in the browser, preventing garbled text display and ensuring the file has the correct extension. tempfile()generates a unique filename every time, eliminating race conditions from concurrent requests.
内容的提问来源于stack exchange,提问作者Zakir Hussain
相关产品推荐
相关产品推荐

