最近开始学习Flex开发,遇到一个需求:上传图片之前需要在本地先预览图片。但是有两个问题:
1.flex4里面还不支持对*.tif 和 *.tiff格式的预览,一方面可能是因为tif图的体积比较大,非常耗内存,另一方也有可能是因为tif图片的格式比较复杂。
2.Jpeg格式的图片中颜色通道主要分为三种:rgb,cmyk,grey。而cmyk的图片在浏览器中显示时颜色会失真。
问题1没有想到好的解决的办法,虽然也在github上找到一个开源的读取tif图片的类库,但是体积过大,预览的时间很慢。关键的是tif格式的图片中颜色通道为cmyk的还是不能正确的显示。
下面是部分代码,及测试效果:
import mx.controls.Alert;
import com.utils.Tiff.TIFF6Decoder;
private var tiffDecoder:TIFF6Decoder;
private var byteArray:ByteArray;
private function loadFile():void {
var request:URLRequest = new URLRequest("images/1.tif");
var urlLoader:URLLoader = new URLLoader(request);
urlLoader.addEventListener(Event.COMPLETE, onLoadComplete);
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.load(request);
}
private function onLoadComplete(e:Event):void {
byteArray = e.target.data;
tiffDecoder = new TIFF6Decoder();
if (tiffDecoder.decode(byteArray)) {
img.source = new Bitmap(tiffDecoder.bitmapData);
}
else {
Alert.show("Failed TIFF decoding");
}
}

问题2的解决办法是先判断Jpeg图片的颜色通道,如果是cmyk的,不显示预览图,上传之后显示后台返回的缩略图。本文的目标就是要解决这个问题。
上传前读取图片的width和height,网上已经有很多方法了。这一点bitmapdata自己就可以做到,甚至不需要别的类库。
示例:
//打开浏览文件窗口选择要上传的文件
private function browse(event:MouseEvent):void {
var imageTypes:FileFilter = new FileFilter("图片 (*.jpg, *.jpeg, *.gif, *.png)", "*.jpg; *.jpeg; *.gif; *.png");
var allTypes:Array = new Array(imageTypes);
fileReferenceList.browse(allTypes);
}
// 选择文件后的处理
// 若选择多个文件,每个文件都需要一个FileReference进行处理
private function selectHandler(event:Event):void {
for (var i:int = 0; i < fileReferenceList.fileList.length; i++) {
var f:FileReference = FileReference(fileReferenceList.fileList[i]);
//每个文件对应的fileReference 监听上传成功后, 后台返回参数的事件
f.addEventListener(Event.COMPLETE, loadCompleteHandler);
f.load();
}
trace("selectHandler Called!");
}
// 本地预览
private function loadCompleteHandler(event:Event):void {
var loader:Loader = new Loader();
var file:FileReference = event.target as FileReference;
if (file.size > 1024 * 1024 * 100) {
Alert.show("文件不能超过100M.", "错误");
return;
}
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function():void {
var bmp:Bitmap = loader.content as Bitmap;
var bmd:BitmapData;
var scale:Number, Number, height:Number;
trace("imgWidth:" + bmp.width + " imgHeight:" + bmp.height );
file.removeEventListener(Event.COMPLETE,loadCompleteHandler);
});
loader.loadBytes(event.target.data);
trace("loadCompleteHandler Called!");
}
但是这个方法也只能处理图片的大小,不能判断colorspace。一般图片的文件头部分存储了关于这个图片的所有相关信息,要想判断Jpeg的colorspace,当然先得了解一下Jpeg的文件头格式。
不看不知道,没想到文件头格式这么复杂。不过还好,colorspace字段的存储位置还是相对固定的:

由上图我们可以看到Jpeg中的S0F0字段存储了colorspace信息,但在S0F0之前可能有一些APPn字段,这些字段需要先跳过去。因此,知道了colorspace的存放位置之后,代码如下:
package com {
import flash.net.URLStream;
import flash.net.URLRequest;
import flash.events.Event;
import flash.events.ProgressEvent;
import flash.utils.Endian;
public class JpegColorSpaceExtractor extends URLStream {
public static const PARSE_COMPLETE : String = "parseComplete";
public static const PARSE_FAILED : String = "parseFailed";
protected var jumpLength : uint;
protected var stopWhenParseComplete : Boolean;
protected var address : int;
protected var dataLoaded : uint;
protected var jpgWidth : uint;
protected var jpgHeight : uint;
protected var jpgColorSpace : int;
// 构造函数
public function JpegColorSpaceExtractor() {
// 设置字节序
endian = Endian.BIG_ENDIAN;
}
protected function progressHandler( e:ProgressEvent ) : void {
// bytesAvailable 当前加载进来的数据
dataLoaded = bytesAvailable;
var seg1: uint = 0;
var seg2: uint = 0;
while ( bytesAvailable ) {
var match : Boolean = false;
if ( jumpLength == 0 ) {
seg1 = readUnsignedByte( );
address++;
if(seg1 == 0xff){
seg2 = readUnsignedByte( );
address++;
if(seg2 >= 0xE0 && seg2 <= 0xEF){
jumpLength = readUnsignedShort( ) - 2;
address += 2;
match = false;
}
if(seg2 >= 0xC0 && seg2 <= 0xCF){
readUnsignedShort( ); // 0x00 ox11
address += 2;
jumpLength = 0;
trace("find!!!");
match = true;
}
}
}
if ( jumpLength > 0 ) {
if ( bytesAvailable >= jumpLength ) {
jumpBytes( jumpLength );
jumpLength = 0;
}
else break;
}
if(match){
readUnsignedByte(); // bit depth
jpgHeight = readUnsignedShort( ); // height
jpgWidth = readUnsignedShort( ); // width
jpgColorSpace = readUnsignedByte(); // colorSpace
address += 6;
removeEventListener( ProgressEvent.PROGRESS, progressHandler );
if ( stopWhenParseComplete && connected ){
close();
}
dispatchEvent( new Event( PARSE_COMPLETE ) );
break;
}
}
}
protected function jumpBytes( count : uint ) : void {
for ( var i : uint = 0; i < count; i++ ) {
readByte( );
address++;
}
}
protected function fileCompleteHandler( e : Event ) : void {
if ( !jpgWidth || !jpgHeight || !jpgColorSpace ) dispatchEvent( new Event( PARSE_FAILED ) );
}
public function extractJpegColorSpace( fileURL : String, stopWhenParsed : Boolean = true ) : void {
addEventListener( ProgressEvent.PROGRESS, progressHandler );
addEventListener( Event.COMPLETE, fileCompleteHandler );
address = 0;
dataLoaded = 0;
jumpLength = 0;
jpgColorSpace = 0;
stopWhenParseComplete = stopWhenParsed;
super.load( new URLRequest( fileURL ) );
}
public function get loaded( ) : uint {
return dataLoaded;
}
public function get width( ) : uint {
return jpgWidth;
}
public function get height( ) : uint {
return jpgHeight;
}
public function get colorSpace(): uint{
return jpgColorSpace;
}
}
}
资料:
Image-MetaData-Jpeg:https://metacpan.org/release/Image-MetaData-JPEG
Getting Jpeg Dimensions: http://www.anttikupila.com/flash/getting-jpg-dimensions-with-as3-without-loading-the-entire-file/