【问题标题】:Streaming Excel data using <cfcontent> tag leads to corrupted excel file使用 <cfcontent> 标记流式传输 Excel 数据会导致 Excel 文件损坏
【发布时间】:2016-09-16 20:29:15
【问题描述】:

我正在尝试通过以下方式发送 excel 数据

<cfsavecontent variable="xlsOutput">
  //My data
</cfsavecontent>

<cfsetting enablecfoutputonly="no" showDebugOutput="false">

<cfheader name="ContentDisposition value='attachment;filename="#xls_file#.xls"'>

<cfcontent type="application/vnd.ms-excel" reset="true" variable="#ToBinary( ToBase64(xlsOutput) )#">

虽然附件以 excel 格式很好地出现,但当我尝试打开它时,它会引发以下错误

文件已损坏,无法打开

出现此错误消息后,文件将被关闭,甚至不显示其内容。

我知道我很接近,但我不确定我在哪里弄错了?

【问题讨论】:

  • (编辑)不是讽刺,但你在发帖前搜索过那个错误信息吗?关于该错误的常见原因有很多线程。通常与 Excel 安全设置有关。此外,如果那是实际代码,cfheader 会害羞结束引号,并且标题名称中有错字。
  • 嗨,Leigh.. 是的,我确实检查了论坛中的各种其他相关问题,但从未找到正确的问题。如果你能看到相关的东西,请引导我解决那个特定的问题,你可以关闭这个线程。我只需要一个正确答案来回答我真正的问题,仅此而已:)

标签: coldfusion coldfusion-9 coldfusion-10 coldfusion-8


【解决方案1】:

我认为您的 toBinary() 和 tobase64() 可能会妨碍您。

这是一个使用 cfspreadsheet、电子表格AddColumn 和 SpreadsheetAddRows 的工作示例。

<!--- FILE --->
<cfset myFile = expandPath('accountslog_#dateFormat(url.datefrom, 'yyyy-mm-dd')#_#dateFormat(dateAdd('d',1,url.dateto), 'yyyy-mm-dd')#.xls') />

<cfset thisSheet = SpreadsheetNew("accountLog") />

<cfquery name="all">
SELECT
        accountlog.dateTime
      , accounts.firstname
      , accounts.lastname
      , accounts.email
      , ... more columns
FROM  accountlog INNER JOIN accounts on accountlog.accountid = accounts.id
WHERE dateTime 
         BETWEEN  <cfqueryparam value="#url.dateFrom#" cfsqltype="cf_sql_timestamp"> 
         AND   <cfqueryparam value="#dateAdd('d',1,url.dateTo)#" cfsqltype="cf_sql_timestamp"> 

    <cfif url.accountid neq ''> 
      AND accounts.id = <cfqueryparam value="#url.accountid#" cfsqltype="cf_sql_varchar"> 
    </cfif>
ORDER BY datetime
</cfquery>

<cfset thisTable = 'accountLog'>
<cfset thisSheet = SpreadsheetNew( thisTable ) />

<!--- COLUMN HEADERS, droooool --->
<cfloop array="#all.getMetaData().getColumnLabels()#" index="c">
    <cfset spreadsheetAddColumn(thisSheet, c) />
</cfloop>

<!--- ADD THE DATA --->
<cfset SpreadsheetAddRows(thisSheet, all) />

<!--- SAVE THE SHEET --->
<cfspreadsheet
    action="update"
    filename="#myFile#"
    name="thisSheet"
    sheetname="#thisTable#" />

<cfheader name="Content-Disposition" value="attachment; filename=#listLast(myFile, '\')#" />
<cfcontent type="application/msexcel" reset="yes" file="#myFile#" deleteFile="yes" />

【讨论】:

  • 生成临时文件时,请务必使用唯一文件名。否则,该文件可能会被同时运行的另一个线程覆盖。保留显示名称的日期戳逻辑,但使用独特的东西,例如物理文件的 createUUID()。此外,如果您只是编写一次性文件,请使用 SpreadSheetWrite() 或 &lt;cfspreadsheet action=write&gt;。根据我的经验cfspreadsheet action=update is buggy,无论如何这里都不需要。
【解决方案2】:

很久以前,我们使用以下技术实现了这一点。现在我将查看 cfspreadsheet 标记。

这是我们所做的:首先将变量保存到 htm 文件,然后使用 excel 打开它。它仍然会给用户一个看起来不太好看的问题:“您尝试打开的文件是由扩展名指定的不同格式,等等......”。下面是解决方案...

<cfsavecontent variable="xlsOutput">
  //My data
</cfsavecontent>

<cffile action="WRITE" file="D:\wwwroot\yourwebsite\test.htm" output="#ToBinary( ToBase64(xlsOutput) )#">

<cfheader name="Content-Type" value="xls">
<cfheader name="Content-Disposition" value="attachment; filename=report.xls">
<cfcontent type="application/vnd.ms-excel" file="D:\wwwroot\yourwebsite\test.htm" deletefile="No"> 

【讨论】:

  • 它仍然会给用户一个问题... 用另一个错误消息替换不是真正的解决方案 ;-) 增强的安全功能使更新版本的 Excel对旧的“欺骗浏览器认为 html 是电子表格”技巧的容忍度要低得多。唯一可靠的解决方案是生成一个真实电子表格。
  • 我同意,这是一种解决方法,不太适合商业应用。我不相信 CF 在 cfspreadsheet 标记之前与 Excel 配合得很好......此外,我的示例中生成的文件实际上会在提示后由 excel 打开,而不是问题中的致命错误。
  • 一切都取决于版本。有些人仍然会遇到问题。旧的 html 技巧 曾是 一个可行的选项。不幸的是,随着新版本的容忍度越来越低,它越来越有可能导致错误或可怕的警告,这不是一个令人愉快的用户体验。最重要的是,您的第一个建议是更好的选择,即创建一个真正的电子表格。
猜你喜欢
  • 2019-11-29
  • 1970-01-01
  • 2018-11-14
  • 1970-01-01
  • 2019-10-14
  • 1970-01-01
  • 2021-02-15
  • 2018-08-01
  • 1970-01-01
相关资源
最近更新 更多