Flex 多图片压缩上传

转自:http://my.oschina.net/eatsuger/blog/85893

 

前段时间按照公司要求做了一个Flex多图片压缩上传组件,使网站实现了图片的快速上传与分享,提高了用户体验。

使用Flex4开发,Flex好像没有自带的多线程功能,因此借用了 开源项目async-threading实现多线程并发上传。

google code地址:http://code.google.com/p/async-threading/

要在flex4 sdk环境下使用,要先修改一下源代码,打开com.symantec.premiumServices.asyncThreading.handlers.FPSObserverHandler


将import mx.core.Application; 修改为import spark.components.Application;


将private var _appRef:Application = Application.applicationas Application;修改为private var _appRef:Application = FlexGlobals.topLevelApplication as Application;


同时导入import mx.core.FlexGlobals;
        这个api要求自定义的线程继承AbstractAsyncThread然后实现IAsyncThreadResponder接口。

首先自定义一个 CommendThread.as

 

package common
{
	import com.symantec.premiumServices.asyncThreading.abstract.AbstractAsyncThread;
	import com.symantec.premiumServices.asyncThreading.interfaces.IAsyncThreadResponder;
	public class CommendThread extends AbstractAsyncThread implements IAsyncThreadResponder
	{
		private var _f:Function;
		public function CommendThread(f:Function)
		{
			this._f = f;
		}
		public function execute():void
		{
			_f.call();
			this.kill();
		}
	}
}
 

 

定义图片组件 MyImage.mxml

 

<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009" 
				xmlns:s="library://ns.adobe.com/flex/spark" 
				xmlns:mx="library://ns.adobe.com/flex/mx" 
				autoDrawBackground="true">
	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
		]]>
	</fx:Script>
	<s:Image width="100" height="100">
		<s:creationComplete>
			<![CDATA[
			var img:Image = event.target as Image;
			var file:FileReference = data as FileReference;
			img.source = file.data;
			]]>
		</s:creationComplete>
	</s:Image>
	<s:Image x="84" y="0" width="16" height="15" source="@Embed('close_btn.png')">
		<s:click>
			<![CDATA[
			parentDocument.valiUpload();
			var file:FileReference = data as FileReference;
			parentDocument.removeImageArray(file); 
			]]>
		</s:click>
		<s:mouseOver>
			<![CDATA[
			Mouse.cursor = MouseCursor.BUTTON;
			]]>
		</s:mouseOver>
		<s:mouseOut>
			<![CDATA[
			Mouse.cursor = MouseCursor.ARROW;
			]]>
		</s:mouseOut>
	</s:Image>
</s:ItemRenderer>
 

 

主文件 flexMultiUpload.mxml

 

<?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"
			   width="700" height="450" minWidth="955" minHeight="600" initialize="init()">
	<fx:Style>
		@namespace s "library://ns.adobe.com/flex/spark";
		@namespace mx "library://ns.adobe.com/flex/mx";
		.back
		{
			backgroundColor:#E7E7E7;
			borderAlpha:0;
		}
	</fx:Style>
	<fx:Script>
		<![CDATA[
			import common.CommendThread;
			
			import mx.collections.ArrayCollection;
			import mx.controls.Alert;
			import mx.graphics.codec.JPEGEncoder;
			import mx.utils.StringUtil;
			
			import spark.components.Image;
			
			
			private var fileRef:FileReferenceList = new FileReferenceList();
			
			[Bindable]
			private var imageArray:ArrayCollection = new ArrayCollection;//图片数组	
			private var imageNameArray:ArrayCollection = new ArrayCollection;//上传图片名称数组
			
			private var uploadURL:String = "http://www.leku.com/flex/flexuploadimage?";
			private var header:URLRequestHeader = new URLRequestHeader("Content-type", "application/octet-stream");
			private var isUploading:Boolean = false;
			
			//初始化
			public function init():void
			{
				//初始化图片上传的url
				var url:String = this.parameters.url;
				if(url != null && url.length>0)
				{
					uploadURL = decodeURI(this.parameters.url);
				}
				
				//初始化文件选择事件
				fileRef.addEventListener(Event.SELECT,selectFileHandler);
				fileRef.addEventListener(Event.COMPLETE,completeFileHandler);
			}
			
			public function valiUpload():void
			{
				if(isUploading)
				{
					Alert.show("正在上传中,请稍后操作。");
					throw new Error("正在上传中,请稍后操作。");
				}
			}
			
			
			//打开图片选择框
			private function showSelectDialog():void
			{
				valiUpload();
				fileRef.browse([
					new FileFilter("Images (*.jpg, *.jpeg, *.gif, *.png)", "*.jpg;*.jpeg;*.gif;*.png")
				]);
			}
			
			
			//图片选择完成后
			private function selectFileHandler(e:Event):void
			{
				for each (var file:FileReference in fileRef.fileList) 
				{
					if(imageNameArray.contains(file.name))
					{
						continue;
					}
					file.load();
					file.addEventListener(Event.COMPLETE,completeFileHandler);
				}
				uploadButton.visible = true;
			}
			
			
			//图片加载完成后
			private function completeFileHandler(e:Event):void
			{
				var file:FileReference = e.target as FileReference;
				insertImageArray(file);
			}
			
			
			//图片数组增加元素
			private function insertImageArray(file:FileReference):void
			{
				imageArray.addItem(file);
				imageNameArray.addItem(file.name);
			}
			
			
			//删除图片数组元素
			public function removeImageArray(file:FileReference):void
			{
				var index:Number = imageArray.getItemIndex(file);
				if(index >= 0)
				{
					imageArray.removeItemAt(index);
				}
				index = imageNameArray.getItemIndex(file.name);
				if(index >= 0)
				{
					imageNameArray.removeItemAt(index);
				}
			}
			
			
			
			private var value:Number = 0;
			private var total:Number = 0;
			private var index:Number = 0;
			private var maxSize:Number = 1024;//设置图片压缩尺寸
			
			//上传图片
			private function upload():void
			{
				if(imageArray.length <= 0)
				{
					Alert.show("请选择图片");
					return;
				}
				valiUpload();
				isUploading = true;
				value = 0;
				total = imageArray.length;
				progressBar.visible = true;
				progressBar.setProgress(value,total);
				for each (var file:FileReference in imageArray) 
				{
					uploading(file);	
				}
				
			}
			
			private function uploading(file:FileReference):void
			{
				new CommendThread(function():void
				{
					var fileName:String = file.name;
					var loader:Loader = new Loader();
					loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function():void
					{
						var bm:Bitmap=loader.content as Bitmap;
						var bmd:BitmapData;
						if((bm.width>=bm.height)&&bm.width>maxSize)
						{
							var rateW:Number=maxSize/bm.width;
							var h:Number=rateW*bm.height;
							bmd=new BitmapData(maxSize,h,true,0);
							bmd.draw(bm.bitmapData,new Matrix(rateW,0,0,rateW),null,null,null,true);
							bm=new Bitmap(bmd,PixelSnapping.ALWAYS,true);
							bmd = bm.bitmapData;
						}
						else if(bm.height>bm.width&&bm.height>maxSize)
						{
							var rateH:Number=maxSize/bm.height;
							var w:Number=rateH*bm.width;
							bmd=new BitmapData(w,maxSize,true,0);
							bmd.draw(bm.bitmapData,new Matrix(rateH,0,0,rateH),null,null,null,true);
							bm=new Bitmap(bmd,PixelSnapping.ALWAYS,true);
							bmd = bm.bitmapData;
						}
						else
						{
							bmd = bm.bitmapData;
						}
						
						
						var urlLoader:URLLoader = new URLLoader();
						var jpeg:JPEGEncoder = new JPEGEncoder(80);
						var b:ByteArray = jpeg.encode(bmd);
						var subRequest:URLRequest = new URLRequest();
						subRequest.method = URLRequestMethod.POST;
						subRequest.requestHeaders.push(header);
						subRequest.url = uploadURL+"&fileName="+encodeURI(fileName==null?"":fileName);//url编码,不然汉字会乱码
						subRequest.data = b;
						
						urlLoader.load(subRequest);  
						urlLoader.addEventListener(Event.COMPLETE,function(e:Event):void{
							progressBar.setProgress(++value,total);
							ExternalInterface.call("flexCallBack",urlLoader.data);
							if(value == total)
							{
								imageArray.removeAll();
								imageNameArray.removeAll();
								value = 0;
								total = 0;
								progressBar.visible = false;
								ExternalInterface.call("flexCompleteCallBack");
								isUploading = false;
							}
//							System.gc();
						});
					});
					loader.loadBytes(file.data);
				}).start();
			}
			
			
		]]>
	</fx:Script>
	<s:VGroup x="0" y="0" width="700" height="450" gap="0">
		<s:HGroup width="700" height="400" gap="0">
			<s:Scroller>
				<s:DataGroup width="684" height="400" dataProvider="{imageArray}"
							 focusColor="#EE7091" itemRenderer="mycomponent.MyImage">
					<s:layout>
						<s:TileLayout useVirtualLayout="true"  requestedColumnCount="6">
						</s:TileLayout>
					</s:layout>
				</s:DataGroup>
			</s:Scroller>
		</s:HGroup>
		<s:BorderContainer width="700" height="50"  styleName="back">
			<s:Button x="210" y="8" width="116" height="35" label="选择图片" click="showSelectDialog()">
				<s:mouseOver>
					<![CDATA[
					Mouse.cursor = MouseCursor.BUTTON;
					]]>
				</s:mouseOver>
				<s:mouseOut>
					<![CDATA[
					Mouse.cursor = MouseCursor.ARROW;
					]]>
				</s:mouseOut>
			</s:Button>
			<s:Button x="370" y="8" width="116" height="35" label="图片上传"  click="upload()" id="uploadButton" visible="false">
				<s:mouseOver>
					<![CDATA[
					Mouse.cursor = MouseCursor.BUTTON;
					]]>
				</s:mouseOver>
				<s:mouseOut>
					<![CDATA[
					Mouse.cursor = MouseCursor.ARROW;
					]]>
				</s:mouseOut>
			</s:Button>
		</s:BorderContainer>
	</s:VGroup>
	<mx:ProgressBar x="150" y="200" width="400" id="progressBar" mode="manual" labelPlacement="center" label="%1 of %2,总进度:%3%%" visible="false">
		
	</mx:ProgressBar>
</s:Application>
 

 

以下是工程结构

Flex 多图片压缩上传_第1张图片

 

 

 

这是close_btn.png的图片:

 

接下来就是要导出swf文件了,如果方式不对的话,会导致文件很大,从何造成网页组件加载很慢。

我是用的是flash builder开发,流程如下

右键点击工程-->选择Properties -->选择 Flex Build Path-->选择RSL选择,这样编译的时候只会加载使用到的类库,大大

降低swf文件大小-->点击OK

 

Flex 多图片压缩上传_第2张图片

 

 

点击Export Release Build

Flex 多图片压缩上传_第3张图片

 

 

选择工程,然后点击finish,完成后在bin-release目录下找到flexMultiUpload.swf

Flex 多图片压缩上传_第4张图片

 

 

可以使用浏览器直接打开,或者flash客户端(尽量把flash更新到最新版本)

服务端使用java struts2

import java.io.File;
import java.io.InputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.FileUtils;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.convention.annotation.Action;
import com.opensymphony.xwork2.ActionSupport;

public class FlexAction  extends ActionSupport{
	

	/**
	 * 
	 */
	private static final long serialVersionUID = -3543364166243030722L;
	private HttpServletRequest request = ServletActionContext.getRequest();
	private HttpServletResponse response = ServletActionContext.getResponse();
	public static final Integer MAX_SIZE = 1024*1024*2;//允许图片最大尺寸
	
	private String fileName;
	
	@Action(value = "flexuploadimage")
	public String upload() throws Exception{
		
		InputStream inputStream = request.getInputStream();
		int formlength = request.getContentLength();
		
		//如果图片大于允许的值
		if(formlength > MAX_SIZE){
			//返回异常信息
			response.getOutputStream().println("自定义返回信息,或者跳转到异常页面");
			return null;
		}
		
		
		byte[] formcontent = new byte[formlength];
		int totalread = 0;
		int nowread = 0;
		while (totalread < formlength) {
			nowread = inputStream.read(formcontent, totalread, formlength);
			totalread += nowread;
		}
		
		//将图片写入本地或者图片服务器
		File file = new File("D:/flex", fileName);
		FileUtils.writeByteArrayToFile(file, formcontent);
		
		return null;
	}

	public String getFileName() {
		return fileName;
	}

	public void setFileName(String fileName) {
		this.fileName = fileName;
	}

	


}

 

jsp页面调用

<%@page import="java.net.URLEncoder"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%
request.setAttribute("url", URLEncoder.encode("上传路径", "utf-8"));
%>
<div style="height:600px;width:800px;display:table-cell;vertical-align:middle;text-align:center;">
	<embed src="flexMultiUpload.swf" style="height:450px;width:700px;border: #C9DDEA solid 1px;vertical-align: middle;" flashVars="url=${url }" allowScriptAccess="always">
	<br/>
	<div style="margin-left: -450px;margin-top: 10px;">
		<span>如果不能正常使用,请将Flash升级到最新版本。</span>
	</div>
</div>

 

组件缺陷(由于本文作者偷懒的结果,哈哈):

每一个图片新开一个线程并发上传,如果用户一次性上传的图片过多,可能会造成客户端挂掉的现象。

解决办法:(1)限制每次上传的图片数量

(2)做一个上传队列,几个线程并发执行队列中的任务(可以参考QQ空间相册普通上传功能,注意:不是极速上传)。

(3)flex的多线程是借助于第三方的

 

你可能感兴趣的:(Flex)