问题
I need to export a pie chart and a datagrid in flex 4.5 to Microsoft Excel format. I was able to export the datagrid using as3xls. But it doesn't allow to export any chart or even add an image to excel file. Can anyone recommend me a way of doing this?
回答1:
As far as I know there is no way to export a chart to Excel by means of existing libraries.
I would like to suggest you one specific way to do it. We all well know that current Office files are stored as collections of XML structures. The files are packed as a zip-archive. If you have a look at this stuff, you can see that it is possible just to manipulate some lines in this files to get any table, any chart, any picture you'd like to have.
To do it in Flex you need basic XML-structures and some library to be able to zip the result.
I have used Nochump component to have access to zip-functions.
Here you can read about the structure of Excel-files.
Here are some pictures which show you a possible result you can achieve with this method.
Regarding the application, you should know which files have to be patched:
- sheet1.xml has information about cells of your sheet
- sharedStrings.xml is a dictionary of all strings in the file
- chart1.xml is a description of your chart - you should change only ranges of your data.
To get it working I created an XML-structure with description of the Excel-file-tree. The actual XML-files of this file-tree I put to my projects folder. Then I read all the files to an ArrayCollection and manipulated data of three of them.
After all I packed them to a zip-archive and let user save it to the PC.
Here is the source code:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955" minHeight="600"
creationComplete="onCreationComplete(event)">
<fx:Declarations>
<fx:XML id="fileTree" xmlns="">
<root id=''>
<folder id='_rels'>
<file id='.rels'/>
</folder>
<folder id='docProps'>
<file id='app.xml'/>
<file id='core.xml'/>
</folder>
<folder id='xl'>
<folder id='_rels'>
<file id='workbook.xml.rels'/>
</folder>
<folder id='charts'>
<file id='chart1.xml'/>
</folder>
<folder id='drawings'>
<folder id='_rels'>
<file id='drawing1.xml.rels'/>
</folder>
<file id='drawing1.xml'/>
</folder>
<folder id='theme'>
<file id='theme1.xml'/>
</folder>
<folder id='worksheets'>
<folder id='_rels'>
<file id='sheet1.xml.rels'/>
</folder>
<file id='sheet1.xml'/>
</folder>
<file id='sharedStrings.xml'/>
<file id='styles.xml'/>
<file id='workbook.xml'/>
</folder>
<file id='[Content_Types].xml'/>
</root>
</fx:XML>
</fx:Declarations>
<fx:Script>
<![CDATA[
import flash.utils.ByteArray;
import mx.collections.ArrayCollection;
import mx.collections.XMLListCollection;
import mx.controls.Alert;
import mx.controls.dataGridClasses.DataGridColumn;
import mx.events.FlexEvent;
import mx.rpc.xml.SimpleXMLDecoder;
import nochump.util.zip.*;
private const CELL_LETTERS:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private var xmlLoader:URLLoader = new URLLoader();
private var loaderItemId:int = 0;
private var rootPath:String = "com/excelchart/xmlsource/";
private var xlsxFiles:ArrayCollection = new ArrayCollection();
[Bindable]private var dp:ArrayCollection = new ArrayCollection([
{party:"SPD", y2009:23, y2005:34.2 },
{party:"CDU", y2009:27.3, y2005:27.8 },
{party:"FDP", y2009:14.6, y2005:9.8 },
{party:"The Left Party", y2009:11.9, y2005:8.7 },
{party:"A90/The Greens", y2009:10.7, y2005:8.1 },
{party:"CSU", y2009:6.5, y2005:7.4 },
{party:"Others", y2009:6, y2005:3.9 }]);
protected function onCreationComplete(event:FlexEvent):void
{
init();
traverseXMLList(fileTree, 0, "");
processNextNode();
}
private function traverseXMLList(xml:XML, depth:int, parentPath:String):void
{
var path:String = (parentPath == "") ? xml.@id : parentPath + "/" + xml.@id;
var nodeType:String = xml.name().localName;
xlsxFiles.addItem({name:xml.@id.toString(), type:nodeType, path:path});
for each (var item:XML in xml.children())
traverseXMLList(item, depth + 1, path);
}
private function packZip():void
{
var zipEntry:ZipEntry;
var fileName:String;
var fileData:ByteArray = new ByteArray();
var zipOut:ZipOutput = new ZipOutput();
for (var i:int = 0; i < xlsxFiles.length; i++)
{
var obj:Object = xlsxFiles.getItemAt(i);
if (obj.type == "file")
{
fileName = obj.path;
zipEntry = new ZipEntry(fileName);
fileData.clear();
fileData.writeUTFBytes(obj.content);
zipOut.putNextEntry(zipEntry);
zipOut.write(fileData);
zipOut.closeEntry();
}
}
// end the zip
zipOut.finish();
var file:FileReference = new FileReference();
file.save(zipOut.byteArray, "chart.xlsx");
}
private function init():void
{
xmlLoader.addEventListener(Event.COMPLETE, onXmlLoaderComplete);
xmlLoader.addEventListener(IOErrorEvent.IO_ERROR, onXmlLoaderIOError);
}
private function loadXML():void
{
xmlLoader.load(new URLRequest(rootPath + xlsxFiles.getItemAt(loaderItemId).path));
}
private function onXmlLoaderComplete(event:Event):void
{
xlsxFiles.getItemAt(loaderItemId).content = (event.currentTarget as URLLoader).data;
processNextNode();
}
private function processNextNode():void
{
if (xlsxFiles.length > loaderItemId + 1)
{
loaderItemId++;
if (xlsxFiles.getItemAt(loaderItemId).type == "file")
loadXML();
else
processNextNode();
}
else
this.btnMakeZip.enabled = true;
}
private function onXmlLoaderIOError(evt:IOErrorEvent):void
{
Alert.show("error!");
}
protected function onMakeZipClick(event:MouseEvent):void
{
parseDataGrid();
packZip();
}
protected function isString(input:String):Boolean
{
return isNaN(Number(input));
}
protected function parseDataGrid():void
{
function getStringId(str:String):int
{
var result:int = -1;
for (var i:int = 0; i < stringDictionary.length; i++)
if (stringDictionary.getItemAt(i) == str)
{
result = i;
break;
}
if (result == -1)
{
stringDictionary.addItem(str);
result = stringDictionary.length - 1;
}
return result;
}
//find sheet1 xml
var sheet1XML:XML;
var xlsxFilesItemId:int;
for (i = 0; i < xlsxFiles.length; i++)
if (xlsxFiles.getItemAt(i).name == "sheet1.xml")
{
sheet1XML = new XML(xlsxFiles.getItemAt(i).content);
xlsxFilesItemId = i;
break;
}
//define the size of the DG
var dgHeight:int = myGrid.dataProvider.length;
var dgWidth:int = myGrid.columns.length;
//namespaces for elements
var mainNS:Namespace = new Namespace("http://schemas.openxmlformats.org/spreadsheetml/2006/main");
var x14acNS:Namespace = new Namespace("x14ac", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac");
default xml namespace = mainNS;
sheet1XML.addNamespace(x14acNS);
sheet1XML.dimension.@ref = "A1:" + CELL_LETTERS.charAt(dgWidth) + (dgHeight + 1).toString();
//delete nodes from sheet1XML.sheetData
var sheetDataRowCollection:XMLListCollection = new XMLListCollection(sheet1XML.sheetData.row);
sheetDataRowCollection.removeAll();
//create a String dictionary
var stringDictionary:ArrayCollection = new ArrayCollection();
var stringId:int = 0;
var cellNode:XML, rowNode:XML, value:String, cellAddress:String;
var i:int, j:int;
//add head information
rowNode = new XML();
rowNode.addNamespace(mainNS);
rowNode.addNamespace(x14acNS);
rowNode = <row r="1" spans="1:3"/>;
rowNode.@x14acNS::dyDescent = "0.25";
for (j = 0; j < dgWidth; j++)
{
value = (((myGrid.columns as ArrayList).getItemAt(j) as GridColumn).headerText).toString();
cellAddress = CELL_LETTERS.charAt(j) + "1";
cellNode = new XML();
cellNode = <c r={cellAddress} t="s"><v>{getStringId(value).toString()}</v></c>;
rowNode = rowNode.appendChild(cellNode);
}
sheetDataRowCollection.addItem(rowNode);
//traverse through DG
for (i = 0; i < dgHeight; i++)
{
rowNode = new XML();
rowNode.addNamespace(mainNS);
rowNode.addNamespace(x14acNS);
rowNode = <row r={i + 2} spans="1:3"/>;
rowNode.@x14acNS::dyDescent = "0.25";
for (j = 0; j < dgWidth; j++)
{
value = (myGrid.dataProvider.getItemAt(i)[((myGrid.columns as ArrayList).getItemAt(j) as GridColumn).dataField]).toString();
cellAddress = CELL_LETTERS.charAt(j) + (i+2).toString();
cellNode = new XML();
if (isString(value))
cellNode = <c r={cellAddress} t="s"><v>{getStringId(value).toString()}</v></c>;
else
cellNode = <c r={cellAddress}><v>{value}</v></c>;
rowNode = rowNode.appendChild(cellNode);
}
sheetDataRowCollection.addItem(rowNode);
}
//save sheet1 to xlsxFiles
xlsxFiles.getItemAt(xlsxFilesItemId).content = sheet1XML;
//sharedStrings
var sharedStringsXML:XML;
for (i = 0; i < xlsxFiles.length; i++)
if (xlsxFiles.getItemAt(i).name == "sharedStrings.xml")
{
sharedStringsXML = new XML(xlsxFiles.getItemAt(i).content);
xlsxFilesItemId = i;
break;
}
//delete nodes from sharedStrings.xml
var sharedStringsCollection:XMLListCollection = new XMLListCollection(sharedStringsXML.si);
sharedStringsCollection.removeAll();
//fill the sharedStrings XML
sharedStringsXML.@count = stringDictionary.length;
sharedStringsXML.@uniqueCount = stringDictionary.length;
//var siNode:XML;
for each (var str:String in stringDictionary)
sharedStringsXML.appendChild(<si><t>{str}</t></si>);
//save sharedStrings to xlsxFiles
xlsxFiles.getItemAt(xlsxFilesItemId).content = sharedStringsXML;
//chart1
var chart1XML:XML;
for (i = 0; i < xlsxFiles.length; i++)
if (xlsxFiles.getItemAt(i).name == "chart1.xml")
{
chart1XML = new XML(xlsxFiles.getItemAt(i).content);
xlsxFilesItemId = i;
break;
}
var catLetter:String = CELL_LETTERS.charAt(cbCategories.selectedIndex);
var catAddress:String = "Tabelle1!$" + catLetter + "$2:$" + catLetter + "$" + (dgHeight + 1).toString();
var valLetter:String = CELL_LETTERS.charAt(cbValues.selectedIndex);
var valAddress:String = "Tabelle1!$" + valLetter + "$2:$" + valLetter + "$" + (dgHeight + 1).toString();
default xml namespace = new Namespace("c", "http://schemas.openxmlformats.org/drawingml/2006/chart");
chart1XML.chart.plotArea.pieChart.ser.cat.strRef.f = catAddress;
chart1XML.chart.plotArea.pieChart.ser.val.numRef.f = valAddress;
xlsxFiles.getItemAt(xlsxFilesItemId).content = chart1XML;
//switch back to the default namespace
default xml namespace = new Namespace("");
}
private function onBtnRefresh():void
{
this.mySeries.nameField = cbCategories.selectedItem.dataField;
this.mySeries.field = cbValues.selectedItem.dataField;
}
]]>
</fx:Script>
<s:HGroup x="100" y="50">
<s:VGroup>
<s:DataGrid id="myGrid" width="360" dataProvider="{dp}">
<s:columns>
<s:ArrayList>
<s:GridColumn dataField="party" headerText="Party"/>
<s:GridColumn dataField="y2005" headerText="2005" width="90"/>
<s:GridColumn dataField="y2009" headerText="2009" width="90"/>
</s:ArrayList>
</s:columns>
</s:DataGrid>
<s:HGroup verticalAlign="bottom">
<s:Label text="Categories:" width="70"/>
<s:ComboBox id="cbCategories" dataProvider="{myGrid.columns}" labelField="headerText" selectedIndex="0"/>
</s:HGroup>
<s:HGroup verticalAlign="bottom">
<s:Label text="Values:" width="70"/>
<s:ComboBox id="cbValues" dataProvider="{myGrid.columns}" labelField="headerText" selectedIndex="1"/>
</s:HGroup>
<s:HGroup>
<s:Button id="btnRefresh" label="Bild Chart" click="onBtnRefresh()"/>
<s:Button id="btnMakeZip" label="Export" enabled="false" click="onMakeZipClick(event)"/>
</s:HGroup>
</s:VGroup>
<mx:PieChart id="myChart" width="281" height="277" dataProvider="{dp}" showDataTips="true">
<mx:series>
<mx:PieSeries id="mySeries" field="y2005" nameField="party" labelPosition="inside" explodeRadius=".12" />
</mx:series>
</mx:PieChart>
<mx:Legend dataProvider="{myChart}"/>
</s:HGroup>
</s:Application>
I hope it can help you. Thanks for the interesting question!
来源:https://stackoverflow.com/questions/14722645/export-flex-chart-and-datagrid-to-excel