AIR读取CSV文件并进行保存

Silver 撰写  

最近在帮一个朋友做一个程序,其中用到AIR读取Excel导出的CSV文件进行数据导入数据库的工作,中间碰到了一些问题,最后都可以一一解决,感觉挺有意思,特意拿来个大家分享一下。

完整代码在本贴的最后,完成的效果如下图所示:
csvdrop

具体开发步骤如下

  • 第一步,CSV文件格式设置和导出:
    通常一些业务资料会保存在Excel当中,CSV可以通过Excel文件的”另存为”获得这个格式的文件,不过要注意的是CSV的分隔符在某些机器上是”,”某些是”;”,这个是通过控制面板(”日期,时间语言和区域设置”->”区域和语言选项”->”自定义”->”列表分隔符” )进行设置的。我的操作系统是XP,用的是Excel 2007,导出的时候是”;”。
    可能在别的系统下操作会有所不同,在我的机器上导出的CSV文件是ansi内码。
  • 第二步,获得文件的引用:
    这里使用了两种不同的方式去获得文件的引用(即选择文件),一种是点击一个按钮,另一种是直接把csv文件拖动到界面上。
    点击按钮选择文件更直观写,这部分代码如下:

    private function browseFile():void{
    	var typeFilters:Array = new Array();
    	var typeFilter:FileFilter;
    	typeFilter = new FileFilter("CSV Files(*.csv)", "*.csv");
    	typeFilters.push(typeFilter);
    
            var file:File = new File();
            file.addEventListener(Event.SELECT, importData);
            file.browseForOpen("选择要导入的CSV数据文件",typeFilters);
    }
    

    这里设置了类型过滤器,用户只能选择”csv”结尾的文件,方便在一大堆文件中找出这个文件。

    private function importData(e:Event):void{
         var file:File = new File(e.target.nativePath);
         getFileContent(file);
            	}
    

    选择具体的文件后就可以读取里面的数据了。

    而另外一种方法是把要读取的文件进程序中以完成数据导入,这种方式更直观快捷。

  • 第三步,判断文件编码,然后读取文件:
    之前刚完成程序后进行测试,csv文件里面有大量的中文内容,一开始没判断代码的时候直接使用下面的语句读取文件:

    var fileContent:String = fileStream.readUTFBytes(fileStream.bytesAvailable)

    后来更发现数据进数据库后会是乱码,才知道这个csv文件不是UTF8的格式,是ANSI的,用notepad另存编码为UTF8就可以正常导入数据了。
    追查原因,是因为这个ansi文件是不能正确用UTF的方式去读的。
    那么以后怎么自动识别文件内码并正确读取而不变成乱码呢,于是就上搜索引擎搜索,终于找到一篇文章,抄了上面一段自动判断文件编码的代码。一句话概括之–先判断内容编码格式再按照编码格式去读取其数据就没错了!

    var code:String;
    switch(true){
         case (b1==-1&&b2==-2):
              code = "unicode";
              break;
        case (b1==-2&&b2==-1):
         	  code = "unicodeFFFE";
    	  break;
        case (b1==-17&&b2==-69):
        	   code = "utf-8";
    	   break;
        default:	code = "ansi";
    break;
    }
    var fileContent:String = fileStream.readMultiByte(fileStream.bytesAvailable,code);
    fileStream.close();
    
  • 第四步,导入CSV的数据到数据库中:
    这一步相对简单,首先是删除数据库已有的数据,然后把上一步中读成字符串的文件内容每行解析成一条数据,然后导入数据库中,这里使用了pimento框架的持久层实现方法,可以很容易地在Actionscript端完成CRUD的操作,代码如下:

    删除已有数据,和服务器端JAVA EE里JPA的实现代码几乎一样:

    private function parseCSV():void{
    	var query:Query = entityManager.createQuery("DELETE FROM Owner o WHERE o.orgID=:orgID");
    	query.setNamedParameter("orgID",curOrg.id);
    	query.executeUpdate().addResultHandler(successDeleteOwner);
    	if(owners!=null && owners.length>0){
    		owners.removeAll();
    	}else if(owners==null){
    		owners = new ArrayCollection();
    	}
    }
    

    然后是拆分CSV的文件,并把记录导入数据库

    private function successDeleteOwner(rows:int):void{
    	var csvArray:Array = new Array();
    	var csvLines:Array = _content.split(String.fromCharCode(13,10));
    	csvLines.splice(0,1);
    	for each(var s:String in csvLines){
    		var lineItems:Array = s.split(",");
    		var owner:Owner = new Owner();
    		owner.homeNumber = lineItems[3];
    		owner.orgID = curOrg.id;
    		owner.ownerName = lineItems[1];
    		owner.propertyID =  lineItems[0];
    		owner.smsNumber = lineItems[2];
    		owner.remark = lineItems[4];
    		entityManager.persist(owner).addResultHandler(successCreatedOwner);
    	}
    }
    



是不是觉得很有趣呢,在一个flex页面中就完成了一个独立的功能的前后台实现了,希望这个帖子能引起大家对parsley和pimento这两个框架的兴趣,过段时间我会继续写一些相关的介绍。



完整的程序代码如下:

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
	addedToStage="dispatchEvent(new Event('configureIOC', true))" horizontalAlign="center"
	width="100%" height="100%" creationComplete="listOrginazation()"
	horizontalScrollPolicy="off" verticalScrollPolicy="off"
	nativeDragEnter="dragEnterHandler(event)" nativeDragDrop="dragDropHandler(event)">

	<mx:Script>
		<![CDATA[
			import flash.utils.setTimeout;
			import org.spicefactory.lib.reflect.types.Void;
			import mx.events.ListEvent;
			import mx.events.ItemClickEvent;
			import org.spicefactory.pimento.service.Query;
			import mx.collections.ArrayCollection;
			import org.spicefactory.cinnamon.service.ErrorMessage;
			import mx.controls.Alert;
			import mx.utils.StringUtil;
			import com.coralbay.model.Organization;
			import com.coralbay.model.Owner;
			import org.spicefactory.pimento.service.EntityManager;

			[Inject]
			public var entityManager:EntityManager;

			[Bindable]
			private var organizations:ArrayCollection;

			[Bindable]
			private var curOrg:Organization = null;

			[Bindable]
			private var curOwner:Owner = null;			

			[Bindable]
			private var owners:ArrayCollection;

			private function listOrginazation():void{
				var query:Query = entityManager.createQuery("select o from Organization o Order By o.createTime desc");
				query.getResultList().addResultHandler(onResult);
			}

			private function onResult (orgs:Array) : void {
			    organizations = new ArrayCollection(orgs);
			    if(orgs.length>0){
			    	curOrg = orgs[0];
			    	orgList.selectedIndex = 0;
			    }else{
			    	curOrg = null;
			    }
			}

			private function changeOrg():void{
				curOrg = orgList.selectedItem as Organization;
				listowner(curOrg.id);
			}

			private function listowner(orgId:int):void{
				var query:Query = entityManager.createQuery("select o from Owner o WHERE o.orgID=:orgID");
			    	query.setNamedParameter("orgID",orgId);
			    	query.getResultList().addResultHandler(onownerResult);
			}

			private function onownerResult(ops:Array):void{
				owners = new ArrayCollection(ops);
			}

			private function browseFile():void{  

				var typeFilters:Array = new Array();
				var typeFilter:FileFilter;
				typeFilter = new FileFilter("CSV Files(*.csv)", "*.csv");
				typeFilters.push(typeFilter);

            	var file:File = new File();
            	file.addEventListener(Event.SELECT, importData);
            	file.browseForOpen("选择要导入的CSV数据文件",typeFilters);
        	}  

        	private function importData(e:Event):void{
            	//Alert.show(e.target.nativePath);
            	var file:File = new File(e.target.nativePath);
            	getFileContent(file);
        	}  

			private function dragEnterHandler(evt:NativeDragEvent):void{

				if(evt.clipboard.hasFormat(ClipboardFormats.FILE_LIST_FORMAT)){
					var dropFiles:Array = evt.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
					if(dropFiles.length>1){
						Alert.show("只能选择一份CSV文件");
						return;
					}
					var file:File = dropFiles[0] as File;
					if(file.extension == "csv"){
						NativeDragManager.acceptDragDrop(this);
					}
				}
			} 

			private function dragDropHandler(evt:NativeDragEvent):void{
				//Alert.show("hihi");
				NativeDragManager.dropAction = NativeDragActions.COPY;
				var dropFiles:Array = evt.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
				getFileContent(dropFiles[0]);
			} 

			private var _content:String;
			private function getFileContent(_file:File):void{

				var fileStream:FileStream = new FileStream();
				fileStream.open(_file, FileMode.READ);
				var b1:int = fileStream.readByte();
				var b2:int = fileStream.readByte();
				fileStream.position = 0;
				var code:String;
				switch(true)
				{
					case (b1==-1&&b2==-2):
						code = "unicode";
						break;
					case (b1==-2&&b2==-1):
						code = "unicodeFFFE";
						break;
					case (b1==-17&&b2==-69):
						code = "utf-8";
						break;
					default:
						code = "ansi";
						break;
				}
				//var fileContent:String = fileStream.readUTFBytes(fileStream.bytesAvailable);
				var fileContent:String = fileStream.readMultiByte(fileStream.bytesAvailable,code);
				fileStream.close();
				_content = fileContent;
				parseCSV();
			}

			private function onErrorResult(event:ErrorMessage):void{
				Alert("删除数据失败!");
				listowner(curOrg.id);
			}

			private function parseCSV():void{
				var query:Query = entityManager.createQuery("DELETE FROM Owner o WHERE o.orgID=:orgID");
				query.setNamedParameter("orgID",curOrg.id);
				query.executeUpdate().addResultHandler(successDeleteOwner);
				if(owners!=null && owners.length>0){
					owners.removeAll();
				}else if(owners==null){
					owners = new ArrayCollection();
				}
			}	

			private function successDeleteOwner(rows:int):void{
				var csvArray:Array = new Array();
				var csvLines:Array = _content.split(String.fromCharCode(13,10));
				csvLines.splice(0,1);
				for each(var s:String in csvLines){
					var lineItems:Array = s.split(",");
					var owner:Owner = new Owner();
					owner.homeNumber = lineItems[3];
					owner.orgID = curOrg.id;
					owner.ownerName = lineItems[1];
					owner.propertyID =  lineItems[0];
					owner.smsNumber = lineItems[2];
					owner.remark = lineItems[4];
					entityManager.persist(owner).addResultHandler(successCreatedOwner);
				} 

			} 

			private function successCreatedOwner(owner:Owner):void{
				//nothing to do now
				owners.addItem(owner);
			}

		]]>
	</mx:Script>

	<mx:HBox width="100%" verticalAlign="middle">
		<mx:Label text="选择公司:"/>
		<mx:ComboBox id="orgList" dataProvider="{organizations}" labelField="name" close="changeOrg()"/>
		<mx:Spacer width="100%"/>
		<mx:Button label="清空当前数据并导入新业主资料" click="browseFile()"/>
	</mx:HBox>

	<mx:DataGrid width="100%" dataProvider="{owners}" height="100%" id="ownerDG">
		<mx:columns>
	         <mx:DataGridColumn dataField="propertyID" headerText="房号"/>
	         <mx:DataGridColumn dataField="ownerName" headerText="业主姓名"/>
	         <mx:DataGridColumn dataField="smsNumber" headerText="短信号码"/>
	         <mx:DataGridColumn dataField="homeNumber" headerText="住宅电话"/>
	         <mx:DataGridColumn dataField="remark" headerText="备注"/>
	     </mx:columns>
	</mx:DataGrid>
</mx:VBox>

7 条评论

  1. tsung
    发表了 2009年09月10日 在 9:05 下午 | 永久链接 |

    没怎么用到CSV..读读看..

  2. xiaokongchengxiang
    发表了 2009年09月10日 在 9:10 下午 | 永久链接 |

    支持阿淫 顶一下

  3. peerless
    发表了 2009年09月10日 在 9:10 下午 | 永久链接 |

    很好,很强大

  4. showping
    发表了 2009年09月10日 在 9:22 下午 | 永久链接 |

    阿淫,你应该再弄一个parsley和pimento这两个框架和教程

  5. 发表了 2009年09月11日 在 1:06 上午 | 永久链接 |

    这两个框架早就check下来了,一直没细看过,期待阿淫的教程…

  6. flexing
    发表了 2010年05月13日 在 1:45 下午 | 永久链接 |

    帮助相当大

发表评论

你必须在 登录 后才能发表评论.