01
This commit is contained in:
+239
@@ -0,0 +1,239 @@
|
||||
<cfsilent>
|
||||
<!---https://www.bennadel.com/blog/461-creating-microsoft-excel-documents-with-coldfusion-and-xml.htm--->
|
||||
<!---порядок колонок, как в селекте, не управляется никак
|
||||
Для определения порядка колонок используется поле ATTRIBUTES.titleMap.ordinal, возможно, не самый надежный способ
|
||||
Можно попробовать использовать порядок колонок в массиве query --->
|
||||
<cfparam name="ATTRIBUTES.qRead" type="query"/>
|
||||
<cfparam name="ATTRIBUTES.titleMap" type="struct"/><!---see ../lib/data/field_set.cfm--->
|
||||
<cfparam name="ATTRIBUTES.sheetTitle" type="string" default="Sheet1"/>
|
||||
<cfparam name="ATTRIBUTES.filename" type="string" default="export.xlsx"/>
|
||||
<cfset AVG_SYMBOL_WIDTH=6.5/>
|
||||
<cfset MAX_COL_LENGTH=30/>
|
||||
|
||||
<cfset qRead=ATTRIBUTES.qRead/>
|
||||
|
||||
<!---<cfdump var=#qRead#/>
|
||||
<cfdump var=#ATTRIBUTES.titleMap#/>
|
||||
|
||||
<cfabort/>
|
||||
--->
|
||||
|
||||
<!---<cfsavecontent variable="strXmlData">--->
|
||||
|
||||
</cfsilent><cfheader
|
||||
name="content-disposition"
|
||||
value="attachment; filename=#ATTRIBUTES.filename#"
|
||||
/><cfcontent
|
||||
type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
/><cfoutput><?xml version="1.0"?>
|
||||
<?mso-application progid="Excel.Sheet"?>
|
||||
<!---
|
||||
This is the Workbook root element. This element
|
||||
stores characteristics and properties of the
|
||||
workbook, such as the namespaces used in
|
||||
SpreadsheetML.
|
||||
--->
|
||||
<Workbook
|
||||
xmlns="urn:schemas-microsoft-com:office:spreadsheet"
|
||||
xmlns:o="urn:schemas-microsoft-com:office:office"
|
||||
xmlns:x="urn:schemas-microsoft-com:office:excel"
|
||||
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
|
||||
xmlns:html="http://www.w3.org/TR/REC-html40">
|
||||
<!---
|
||||
The first child element of the WorkBook element
|
||||
is DocumentProperties. Office documents store
|
||||
metadata related to the document—for example,
|
||||
the author name, company, creation date, and
|
||||
more in the DocumentProperties element.
|
||||
--->
|
||||
<DocumentProperties
|
||||
xmlns="urn:schemas-microsoft-com:office:office">
|
||||
<Author>xls-generator</Author>
|
||||
<Company></Company>
|
||||
</DocumentProperties>
|
||||
<!---
|
||||
The Styles node represents information related
|
||||
to individual styles that can be used to format
|
||||
components of the workbook.
|
||||
--->
|
||||
<Styles>
|
||||
<!--- Basic format used by all cells. --->
|
||||
<Style ss:ID="Default" ss:Name="Normal">
|
||||
<Alignment ss:Vertical="Top"/>
|
||||
<Borders/>
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="11"/>
|
||||
<Interior/>
|
||||
<NumberFormat/>
|
||||
<Protection/>
|
||||
</Style>
|
||||
<Style ss:ID="Th">
|
||||
<Alignment ss:Horizontal="Center" ss:Vertical="Top"/>
|
||||
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="11" ss:Bold="1"/>
|
||||
</Style>
|
||||
<Style ss:ID="Int">
|
||||
<NumberFormat ss:Format="0" />
|
||||
</Style>
|
||||
<Style ss:ID="DateTime">
|
||||
<NumberFormat ss:Format="dd/mm/yyyy\ hh:mm;@"/>
|
||||
</Style>
|
||||
<!---
|
||||
<Style ss:ID="ShortDate">
|
||||
<NumberFormat ss:Format="[ENG][$-409]d\-mmm\-yyyy;@" />
|
||||
</Style>
|
||||
|
||||
<Style ss:ID="YesNo">
|
||||
<NumberFormat ss:Format="Yes/No" />
|
||||
</Style>
|
||||
--->
|
||||
</Styles>
|
||||
<!---
|
||||
This defines the first worksheeet and it's name.
|
||||
We are only using one worksheet in this example,
|
||||
but you could add more Worksheet nodes after
|
||||
this one for multiple tabs. The "Name" attribute
|
||||
here is the name that shows up in the tab.
|
||||
--->
|
||||
<Worksheet ss:Name="#ATTRIBUTES.sheetTitle#">
|
||||
<Table
|
||||
<!---
|
||||
We need a column for each column of the
|
||||
query. This attribute is required to be
|
||||
correct. If the value here does NOT
|
||||
match the data in Excel file, the
|
||||
document will not render properly.
|
||||
--->
|
||||
ss:ExpandedColumnCount="#ListLen(qRead.ColumnList)#"
|
||||
<!---
|
||||
We need a row for every query record
|
||||
plus one for the header row. Again, if
|
||||
this value does not match what is in the
|
||||
document, the excel file will not
|
||||
render properly.
|
||||
--->
|
||||
ss:ExpandedRowCount="#(qRead.RecordCount + 1)#"
|
||||
x:FullColumns="1"
|
||||
x:FullRows="1">
|
||||
<!---
|
||||
Here, we can define general properties
|
||||
regarding each column in the data output.
|
||||
|
||||
<Column ss:Index="1" ss:Width="30" />
|
||||
<Column ss:Index="2" ss:Width="100" />
|
||||
<Column ss:Index="3" ss:Width="42" />
|
||||
<Column ss:Index="4" ss:Width="84" />
|
||||
<Column ss:Index="5" ss:Width="66" />
|
||||
<Column ss:Index="6" ss:Width="70" />--->
|
||||
|
||||
<cfsilent>
|
||||
<cfset columns=arrayNew(1)/>
|
||||
<cfset i=1/>
|
||||
<cfloop struct=#ATTRIBUTES.titleMap# item="field">
|
||||
<cfset col=structNew()/>
|
||||
<cfset fieldSpec=structFind(ATTRIBUTES.titleMap, field)/>
|
||||
<cfset col.name=field/>
|
||||
|
||||
<cfset col.title=fieldSpec.title/>
|
||||
<cfif len(col.title) EQ 0>
|
||||
<cfset col.title=col.name/>
|
||||
</cfif>
|
||||
<cfset col.titleLength=len(col.title)/>
|
||||
|
||||
<cfquery name="qLen" dbtype="query">
|
||||
select coalesce(max(length(#field#)),0) as maxlen from qRead
|
||||
</cfquery>
|
||||
<cfset col.length=max(col.titleLength, qLen.maxLen)/>
|
||||
|
||||
<cfset col.cfSqlType="CF_SQL_VARCHAR"/>
|
||||
<cfif structKeyExists(fieldSpec,"cfSqlType")>
|
||||
<cfset col.cfSqlType=#fieldSpec.cfSqlType#/>
|
||||
</cfif>
|
||||
|
||||
<cfswitch expression=#col.cfSqlType#><!---should use ## around variable name--->
|
||||
<cfcase value="CF_SQL_BIGINT,CF_SQL_INTEGER,CF_SQL_SMALLINT,CF_SQL_TINYINT,CF_SQL_BIT,CF_SQL_NUMERIC,CF_SQL_DECIMAL,CF_SQL_REAL,CF_SQL_FLOAT,CF_SQL_DOUBLE">
|
||||
<cfset col.ssType="Number"/>
|
||||
</cfcase>
|
||||
<cfcase value="CF_SQL_TIMESTAMP,CF_SQL_DATE,CF_SQL_TIME,CF_SQL_DATETIME">
|
||||
<cfset col.ssType="DateTime"/>
|
||||
</cfcase>
|
||||
<cfdefaultcase>
|
||||
<cfset col.ssType="String"/>
|
||||
</cfdefaultcase>
|
||||
</cfswitch>
|
||||
|
||||
<cfset columns[fieldSpec.ordinal]=col/><!---new col created at each iteration, so we have valid references--->
|
||||
<cfset i=i+1/>
|
||||
</cfloop>
|
||||
</cfsilent>
|
||||
|
||||
<cfloop from=1 to=#arrayLen(columns)# index="i">
|
||||
<cfif columns[i].ssType EQ "DateTime"><!--- timestamp string representation is too long, --->
|
||||
<Column ss:Index="#i#" ss:Width="#round(min(columns[i].titleLength, MAX_COL_LENGTH)*AVG_SYMBOL_WIDTH)#" ss:AutoFitWidth="1"/>
|
||||
<cfelse>
|
||||
<Column ss:Index="#i#" ss:Width="#round(min(columns[i].length, MAX_COL_LENGTH)*AVG_SYMBOL_WIDTH)#" ss:AutoFitWidth="1"/>
|
||||
</cfif>
|
||||
</cfloop>
|
||||
<!---
|
||||
This is our header row. All cells in the
|
||||
header row will be of type string.
|
||||
--->
|
||||
<Row>
|
||||
<cfloop from=1 to=#arrayLen(columns)# index="i">
|
||||
<Cell ss:StyleID="Th">
|
||||
<Data ss:Type="String">#columns[i].title#</Data>
|
||||
</Cell>
|
||||
</cfloop>
|
||||
</Row>
|
||||
|
||||
<cfflush/>
|
||||
|
||||
<cfloop query=#qRead#><!---
|
||||
---><Row><!---
|
||||
---><cfloop from=1 to=#ArrayLen(columns)# index="i"><!---
|
||||
---><cfset field=columns[i].name/><!---
|
||||
---><Cell<cfif columns[i].ssType EQ "DateTime"> ss:StyleID="DateTime"</cfif>><!---
|
||||
---><cfif len(qRead[field])><Data ss:Type="#columns[i].ssType#"><!---no extra whitespace allowed here---><!---
|
||||
---><cfswitch expression=#columns[i].ssType#><!---
|
||||
---><cfcase value="DateTime"><!---
|
||||
--->#dateFormat(qRead[field],"YYYY-MM-DD")#T#timeFormat(qRead[field],"HH:mm:ss.l")#<!---
|
||||
---></cfcase><!---
|
||||
---><cfcase value="Number"><!---
|
||||
--->#qRead[field]#<!---
|
||||
---></cfcase><!---
|
||||
---><cfdefaultcase><!---
|
||||
---><![CDATA[#request.clean4CDATA(qRead[field])#]]><!---
|
||||
---></cfdefaultcase><!---
|
||||
---></cfswitch><!---
|
||||
---></Data></cfif><!---
|
||||
---></Cell><!---
|
||||
---></cfloop><!---
|
||||
---></Row><!---
|
||||
---></cfloop><!---
|
||||
|
||||
---></Table>
|
||||
|
||||
</Worksheet>
|
||||
|
||||
</Workbook>
|
||||
|
||||
</cfoutput>
|
||||
<!---</cfsavecontent>--->
|
||||
|
||||
<!---
|
||||
Define the way in which the browser should interpret
|
||||
the content that we are about to stream.
|
||||
--->
|
||||
<!---<cfheader
|
||||
name="content-disposition"
|
||||
value="attachment; filename=#ATTRIBUTES.filename#"
|
||||
/>--->
|
||||
|
||||
<!---
|
||||
When streaming the Excel XML data, trim the data and
|
||||
replace all the inter-tag white space. No need to stream
|
||||
any more content than we have to..ReplaceAll( '>\s+', '>' ).ReplaceAll( '\s+<', '<' )
|
||||
--->
|
||||
<!---<cfcontent
|
||||
type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
variable="#ToBinary( ToBase64( strXmlData.Trim()))#"
|
||||
/>---><!---<cfabort/>--->
|
||||
<cfexit method="exittag"/>
|
||||
Reference in New Issue
Block a user