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>
以下是工程结构
这是close_btn.png的图片:
接下来就是要导出swf文件了,如果方式不对的话,会导致文件很大,从何造成网页组件加载很慢。
我是用的是flash builder开发,流程如下
右键点击工程-->选择Properties -->选择 Flex Build Path-->选择RSL选择,这样编译的时候只会加载使用到的类库,大大
降低swf文件大小-->点击OK
点击Export Release Build
选择工程,然后点击finish,完成后在bin-release目录下找到flexMultiUpload.swf
可以使用浏览器直接打开,或者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的多线程是借助于第三方的