[Flex/AIR]BitmapData를 PNG나 JPG로 변환하여 ByteArray로 서버에 전송하는 방법

2009/12/30 11:08

로컬자원을 서버에 전송하기 위해 우리는 FileReference Class를 사용하면 된다.
FileReference로 파일을 서버에 전송하는 방법은 많이 공개가 되어 있다.
알다시피 FileReference의 browse()함수를 호출한 뒤, select 이벤트가 발생시 upload() 함수를 이용하여 선택한 로컬자원을 서버로 보낸다.

서버에서는 아주 단순하게 서버 임시 저장소에 저장된 파일을 원하는 곳에 복사하기만 하면 된다.
php의 경우 move_uploaded_file() 메소드를 사용하면 되겠다.

그럼 Flex 시행도중 캡쳐한 화면을 저장하는 경우에도 위와 같은 방법으로 저장이 가능할까?

답은 "된다"


나는 예전에 ImageSnapshot 클래스를 이용해 base64로 변환해서 서버로 전송한 뒤에 base64를 decode하여 저장하는 방법에 대해서 언급한적이 있다. (http://blog.jidolstar.com/301 참고)
이 방법의 단점은 이미지가 큰 경우 base64로 encode, decode 하는 과정에서 서버성능 및 클라이언트 성능에 따라 속도 및 부하에 영향을 준다. 그러므로 이 방법으로는 PNGEncoder 및 JPGEncoder로 PNG, JPG 파일 형식에 맞는 데이타를 만들었다고 해도, FileReference와 같이 데이타를 전송을 할 수 없었다.

하지만 Google을 열심히 돌아다녔다니 이 문제점에 대한 해결의 실마리를 찾을 수 있었다.
(역시 Google!!!)

간단히 방법을 요약하자면
화면을 BitmapData로 만들어 PNGEncoder나 JPGEncoder를 이용하여 encode한 다음, 그 결과값인 byteArray값을 서버에 전송한다. 전송된 데이타는 FileReference에서 upload()을 이용해 보낸 파일을 저장할때와 동일하게 저장하면 되겠다.

1. BitmapData로 캡쳐해라.

아래 target은 캡쳐할 UIComponent와 같은 DisplayObject 객체이다.

BitmapData로 캡쳐
var bitmapData:BitmapData = new BitmapData(target.width, target.height, true, 0xFFFFFF);
var drawingRectangle:Rectanglenew Rectangle(0, 0, target.width, target.height);
bitmapData.draw(target, new Matrix(), null, null, drawingRectangle, false);

단, BitmapData를 이용해서 화면을 캡쳐할 대상이 외부 동영상이나 사진같은 거라면 crossdomain.xml 에 대한 check가 있어야 한다. 컨텐츠 로드시 checkPolicyFile 속성을  true로 설정할 필요가 있겠다.
그리고 2880px 이상의 크기는 BitmapData로 만들 수 없다.



2. JPG나 PNG로 Encode 하여 ByteArray를 얻는다.


Flex 3 SDK에는 mx.graphics.codec 패키지에 JPGEncoder와 PNGEncoder가 있다. 인터넷을 뒤져보면 GIFEncoder등도 있을것이다. Flex 2 환경에서 작업한다면 Google code에 Adobe AS3 Corelib에 이들이 제공된다. 만약 JPGEncoder를 사용한다면 다음과 같이 하면 되겠다.

JPGEncoder를 이용하여  JPG  ByteArray값 만들기
import mx.graphics.codec.JPGEncoder;

var byteArray:ByteArray = new JPGEncoder().encode(bitmapData);


Flex 3 SDK는 이러한 Encoder가 IImageEncoder로 구현되었다. 필요하다면 언제 어디서나 Encoder를 바꿔야 하는 경우 IImageEncoder를 사용하는 것이 좋을 수 있겠다.
가령 아래 예제처럼 말이다.

다양한 Image Encoder 사용하기
import mx.graphics.codec.*;

var imageType:String = "jpg";
var imageEncoder:IImageEncoder;
if( imageType.toUpperCase() == "JPG" ) imageEncoder= new JPEGEncoder();
else if( imageType.toUpperCase() == "PNG" ) imageEncoder= new PNGEncoder();
var byteArray:ByteArray = imageEncoder.encode(bitmapData);



 

3. 서버에 ByteArray를 전송한다.

데이타를 전송할때는 FileReference를 사용하지 않는다.
바로 URLLoader와 URLRequest만 이용해서 전송이 가능하다. 참고 데이타는 POST방식으로 URLVariable을 이용해서 보낼 수 있다.

Flex/AIR 데이터 전송 방법
//assumed variable declarations
//var byteArray:ByteArray = 2번째 단계에서 JPG 데이타를 얻었다.
//var fileName:String = "desiredfilename.jpg" //저장할 파일 이름이다. 아무거나 적자!
//var uploadPath:String = "저장할 때 사용되는 서버측 script 경로"
//var parameters:URLVariables = 이미지 이외에 다른 보낼 다른 데이타가 있다면 이것을 이용한다.
//function onComplete(eventObj:Event):void {  성공적으로 데이타를 전송했을때 핸들러 함수 정의
//function onError(eventObj:ErrorEvent):void {  이미지 전송을 실패했을때 핸들러 함수 정의

var urlRequest:URLRequest = new URLRequest();
urlRequest.url = uploadPath;
//urlRequest.contentType = 'multipart/form-data; boundary=' + UploadPostHelper.getBoundary();
urlRequest.method = URLRequestMethod.POST;
urlRequest.data = UploadPostHelper.getPostData(file, byteArray, parameters);
urlRequest.requestHeaders.push( new URLRequestHeader( 'Cache-Control', 'no-cache' ) );
urlRequest.requestHeaders.push( new URLRequestHeader( 'Content-Type', 'multipart/form-data; boundary=' +UploadPostHelper.getBoundary()) );

var urlLoader:URLLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.addEventListener(Event.COMPLETE, onComplete);
urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onError);
urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
urlLoader.load(urlRequest);


위에 진한 부분에 대한 클래스는 아래에 정의되어 있다. 당신은 이 클래스가 어떻게 구성되었는가 열심히 공부할 필요가 없다.(원한다면 해도 된다. 말리지 않음 ^^)

UploadPostHelper Class (Language : java)
package {

    import flash.events.*;
    import flash.net.*;
    import flash.utils.ByteArray;
    import flash.utils.Endian;

    /**
     * Take a fileName, byteArray, and parameters object as input and return ByteArray post data suitable for a UrlRequest as output
     *
     * @see http://marstonstudio.com/?p=36
     * @see http://www.w3.org/TR/html4/interact/forms.html
     * @see http://www.jooce.com/blog/?p=143
     * @see http://www.jooce.com/blog/wp%2Dcontent/uploads/2007/06/uploadFile.txt
     * @see http://blog.je2050.de/2006/05/01/save-bytearray-to-file-with-php/
     *
     * @author Jonathan Marston
     * @version 2007.08.19
     *
     * This work is licensed under a Creative Commons Attribution NonCommercial ShareAlike 3.0 License.
     * @see http://creativecommons.org/licenses/by-nc-sa/3.0/
     *
     */

    public class UploadPostHelper {

        /**
         * Boundary used to break up different parts of the http POST body
         */

        private static var _boundary:String = "";

        /**
         * Get the boundary for the post.
         * Must be passed as part of the contentType of the UrlRequest
         */

        public static function getBoundary():String {

            if(_boundary.length == 0) {
                for (var i:int = 0; i < 0x20; i++ ) {
                    _boundary += String.fromCharCode( int( 97 + Math.random() * 25 ) );
                }
            }

            return _boundary;
        }

        /**
         * Create post data to send in a UrlRequest
         */

        public static function getPostData(fileName:String, byteArray:ByteArray, parameters:Object = null):ByteArray {

            var i: int;
            var bytes:String;

            var postData:ByteArray = new ByteArray();
            postData.endian = Endian.BIG_ENDIAN;

            //add Filename to parameters
            if(parameters == null) {
                parameters = new Object();
            }
            parameters.Filename = fileName;

            //add parameters to postData
            for(var name:String in parameters) {
                postData = BOUNDARY(postData);
                postData = LINEBREAK(postData);
                bytes = 'Content-Disposition: form-data; name="' + name + '"';
                for ( i = 0; i < bytes.length; i++ ) {
                    postData.writeByte( bytes.charCodeAt(i) );
                }
                postData = LINEBREAK(postData);
                postData = LINEBREAK(postData);
                postData.writeUTFBytes(parameters[name]);
                postData = LINEBREAK(postData);
            }

            //add Filedata to postData
            postData = BOUNDARY(postData);
            postData = LINEBREAK(postData);
            bytes = 'Content-Disposition: form-data; name="Filedata"; filename="';
            for ( i = 0; i < bytes.length; i++ ) {
                postData.writeByte( bytes.charCodeAt(i) );
            }
            postData.writeUTFBytes(fileName);
            postData = QUOTATIONMARK(postData);
            postData = LINEBREAK(postData);
            bytes = 'Content-Type: application/octet-stream';
            for ( i = 0; i < bytes.length; i++ ) {
                postData.writeByte( bytes.charCodeAt(i) );
            }
            postData = LINEBREAK(postData);
            postData = LINEBREAK(postData);
            postData.writeBytes(byteArray, 0, byteArray.length);
            postData = LINEBREAK(postData);

            //add upload filed to postData
            postData = LINEBREAK(postData);
            postData = BOUNDARY(postData);
            postData = LINEBREAK(postData);
            bytes = 'Content-Disposition: form-data; name="Upload"';
            for ( i = 0; i < bytes.length; i++ ) {
                postData.writeByte( bytes.charCodeAt(i) );
            }
            postData = LINEBREAK(postData);
            postData = LINEBREAK(postData);
            bytes = 'Submit Query';
            for ( i = 0; i < bytes.length; i++ ) {
                postData.writeByte( bytes.charCodeAt(i) );
            }
            postData = LINEBREAK(postData);

            //closing boundary
            postData = BOUNDARY(postData);
            postData = DOUBLEDASH(postData);

            return postData;
        }

        /**
         * Add a boundary to the PostData with leading doubledash
         */

        private static function BOUNDARY(p:ByteArray):ByteArray {
            var l:int = UploadPostHelper.getBoundary().length;

            p = DOUBLEDASH(p);
            for (var i:int = 0; i < l; i++ ) {
                p.writeByte( _boundary.charCodeAt( i ) );
            }
            return p;
        }

        /**
         * Add one linebreak
         */

        private static function LINEBREAK(p:ByteArray):ByteArray {
            p.writeShort(0x0d0a);
            return p;
        }

        /**
         * Add quotation mark
         */

        private static function QUOTATIONMARK(p:ByteArray):ByteArray {
            p.writeByte(0x22);
            return p;
        }

        /**
         * Add Double Dash
         */

        private static function DOUBLEDASH(p:ByteArray):ByteArray {
            p.writeShort(0x2d2d);
            return p;
        }

    }
}



한가지 중요한 정보를 언급하겠다.
URLLoader를 이용해 서버에 전송할때, 프로그램이 같은 도메인상에 있는 경우에는 보안문제가 없다. 하지만 다른 도메인에 위치한 서버로 이미지를 전송할때는 반드시 crossdomain.xml을 check해야한다.

1. Security.loadPolicyFile(http://다른도메인/crossdomain.xml); 를 URLLoader의 load()함수를 호출하기 전에 호출한다.

2. Flash Player 9.0.124.0 버전부터는 HTTP Header 보안취약점을 해결하기 위해서 cross domain 정책이 변경되었는데.... 서버측에 있는 crossdomain.xml에 allow-http-request-headers-from가 추가되어져야 한다. 이것은 HTTP 헤더 전송을 허용할지 결정해준다.

crossdomain.xml (Language : xml)
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
      <allow-access-from domain="*.jidolstar.com" />
      <allow-http-request-headers-from domain="*.jidolstar.com" headers="*"/>
</cross-domain-policy>

위 처럼 서버측 crossdomain.xml에 allow-http-request-headers-from을 추가함으로 다른 도메인간에 HTTP 헤더 전송을 허용할 수 있다.

서로 다른 도메인에 SWF와 서버측 코드가 배치되어 있다면 반드시 이 사실을 숙지하길 바란다.


3. Flash Player 10에서는 사용자 인터렉션이 반드시 필요하다.

다음글을 참고하세요.

http://blog.jidolstar.com/411 


 

4. 서버측 코드 작성

만약 위 3번 코드에서 var parameters:URLVariables를 아래와 같이 작성했다고 하자.

URLVariables 설정 (Language : java)
var parameters:URLVariables = new URLVariables();
parameters.method = "id";
parameters.userId = "2000321";


그럼 PHP 코드로 아래와 같은 방법처럼 만들면 되겠다.(테스트는 안해봤음)

PHP 코드 예제 (Language : php)
<?php
$method = $_POST['method'];
$userId = $_POST['userId'];
$file_temp = $_FILES['Filedata']['tmp_name'];
$file_name = $_FILES['Filedata']['name'];
$file_size = $_FILES['Filedata']['size'];

if( $method == "id" )
{
  $movePath = "/home/myPath/images/$userId_$file_name";
}
else
{
  $movePath = "/home/myPath/images/time_".time()."_".$file_name;
}

move_uploaded_file($file_temp,$movePath);

echo "save Complete";
?>


마지막 save Comple 메시지를 받기 위해서는 Flex의 Complete 이벤트 발생시 아래와 같은 방법으로 받으면 되겠다. 이것을 알아내는데도 많이 힘들었다. 일종의 팁이다. ^^;

데이타 받기 (Language : java)
var loader:URLLoader = URLLoader(event.target);
var bytedata:ByteArray = loader.data;
var strData:String = bytedata.toString();
trace( strData)


실제 업무에도 잘 이용하고 있다.
이미지 에디터 등을 만들때 아주아주 유용한 자료가 될 것이다.

누가 예제 프로그램 만들어서 트랙백 걸어 주시면 고맙겠다.


자료 출처 
http://marstonstudio.com/2007/08/19/how-to-take-a-snapshot-of-a-flash-movie-and-automatically-upload-the-jpg-to-a-server-in-three-easy-steps/


지돌스타 블로그내 참고자료
 - ImageSnapshot 클래스에 대해 : http://blog.jidolstar.com/301
 - FileReference의 UploadCompleteData 이벤트 : http://blog.jidolstar.com/324
 - 동영상 캡쳐 방법 : http://blog.jidolstar.com/215


글쓴이 : 지돌스타(http://blog.jidolstar.com/352)

Adobe Flex / ActionScript 3.0 , , , , , , , , , , , ,

  1. 크어억... 이런 대단한 일을 저지르시다니 ㅎㅎ!

  2. ㅎㅎ 전 그냥 분석해서 정리만 했을뿐입니다. ^^

  3. Goooooooooooooooooooooooooooooooooooooooooooooood Joooooooooob!

  4. ㅎㅎ 유용하져? ^^

  5. Blog Icon
    동강

    좋은 자료 잘 보았습니다.ㅎ 시간 날때 적용해 봐야 겠어요.

  6. 좋은 글 잘 보았습니다.
    예제 프로그램 트랙백 걸었습니다. ^^
    http://lostsin.tistory.com/96

  7. 좋은 예제 고마워요~ ^^

    위에
    var imageType:String = "jpg";
    var imageEncoder:IImageEncoder;
    if( imageType.toUpperCase() == "JPG" ) imageEncoder= new JPEGEncoder();
    else if( imageType.toUpperCase() == "PNG" ) imageEncoder= new PNGEncoder();
    var byteArray:ByteArray = imageEncoder.encode(bitmapData);

    이렇게 수정했어요.
    해보니 안되더라구요... ㅎㅎㅎ

  8. 앗 네
    저도 그 부분은
    JPGEncoder => JPEGEncoder
    이렇게 수정해서 했었네요^^;

  9. ㅎㅎㅎ 테스트 안해보고 쓴부분이라 오류가 있었습니다. ^^

  10. Blog Icon
    정의

    진짜 대박...짱입니다....!!!!!!!!!!!!!

  11. 제가 봐도 좋은 정보입니다. ㅎㅎ

  12. Blog Icon
    czar

    좋은 정보 감사합니다.
    블로그에 퍼갔습니다. 꾸벅.

  13. Blog Icon
    짱아

    담아갑니다 감사합니다 ^^

  14. 네트웍 어플에 관심을 갖게 되시면 프로토콜을 이해하실 필요가 있죠.
    통상적으로 사용하는 http프로토콜은 request와 response가 전부 텍스트로 구성되는 프로토콜입니다.
    request 프로토콜은 헤더부분과 본문부분으로 나뉘는데(엔터 두번을 키로해서) post방식으로 데이터를 보내는 경우 request의 본문 부분에 데이터를 cgi문자열 방식으로 기술하게 됩니다.
    하지만 requset본문에 직접 바이너리 데이터를 기술할 수도 있는데 이런 경우 request의 헤더부분에 이하 본문이 바이트스트림이라는 것을 명시해야합니다.

    contentType= application/octet-stream

    요렇게 하죠. 이걸 플래시에서 할 때는

    request.method = URLRequestMethod.POST;
    request.contentType = 'application/octet-stream';
    request.data = byteArray;

    요렇게 합니다.
    일단 이렇게 수신된 데이터는 서버측에서 request를 조사할 때 php를 예를 들자면 다음과 같이 찾아집니다.

    $jpg = $GLOBALS["HTTP_RAW_POST_DATA"];

    이게 사실 가장 간단하죠. 하지만 파일을 여러개 보내거나 변수값도 같이 보내고 싶을 때가 많습니다.
    이런 경우 동시에 여러가지 타입을 보내기 위해 헤더에 다음과 같은 멀티타입을 선언합니다.

    contentType=multipart/form-data, boundary='+boundary;

    여기서 이해하실 부분이 바운더리인데 바운더리에는 적당히 긴 문자열을 넣어주시면 됩니다. 이 바운더리는 request본문을 split 하기 위한 키가 됩니다. 서버는 이 키를 바탕으로 본문을 구분지어 개별로 데이터를 인식하여 값과 바이너리를 동시에 처리하죠. 말그대로 멀티파트인 셈입니다.

    멀티파트 폼에 대한 스펙은 공개되어있는데 간단히 구조를 보면

    1. 대쉬+바운더리+엔터 로 섹션을 선언하고

    2.
    값의 경우엔 'Content-Disposition: form-data; name=변수명' 을 적고
    엔터를 두번 쓰고 나서 값을 쓰고 다시 엔터를 적습니다.

    3. 바이터리의 경우엔 1번은 동일하고
    'Content-Disposition: form-data; name=변수명; filename=파일이름' 을 적고 엔터를 적은 뒤
    'Content-Type: application/octet-stream' 이걸 추가로 적어준뒤 엔터 두 번을 적고 나서
    byteArray.readByte를 통해 쭉 적어주면 됩니다. 마찬가지로 끝나면 엔터한번

    4.
    1-2, 1-3 을 반복하여 원하는 모든 값을 request의 본문에 작성합니다.

    멀티파트로 보낸 데이터는 php에선 $_FILE['변수명'] 정도에서 읽어들입니다. 이거야 워낙 흔한 소스니 넘어가죠.




    위에 업어오신 헬프 클래스는 멀티파트형식을 지원하죠.
    멀티파트던 옥텟스트림이던 메소드는 post입니다. request가 본문에 추가적으로 내용을 기술한다는 의미죠.

    request.method = URLRequestMethod.POST;

    정도입니다.

    이상에서 살펴본 프로토콜 스펙에 따라 request는 다음과 같은 경우로 나뉩니다.

    1. get만 쓰는 경우
    2. post인데 옥텟스트림만 쓰는 경우
    3. post인데 변수값만 보내는 경우
    4. post인데 멀티파트로 보내는 경우

    해서 이 모든게 귀찮기 때문에 이모든 경우를 래핑하는 request생성기를 하나 만들어두시면 편리합니다.

    대략 인자객자를 통한 호스트 코드는 아래와 같습니다.

    CSloader.LOAD( CSloader.ARG.END( Inited )
        .addINI( 'ini', '9.php' ).POST( {key:key, name:name} ).FILE( byteJpg )
        .addCLASS_stage( 'cls', 'title.swf' )
    );

    그러니까 동시에 다중로딩을 지원하는거야 CSloader의 능력이지만 내부적으로 CSloader는 CloaderInfo를 순차적으로 생성하여 로딩을 한번에 하나씩 진행해줍니다. 전부 끝나면 END에 보고하는 식이죠.
    이때 개별 요청에 대해 .POST() .GET() .FILE() 을 덧붙여줄 수 있습니다.
    .FILE이 없으면 위에 설명한 3번처럼 처리하고, FILE만 있으면 2번으로 처리하고 둘다 있으면 4번으로 처리하며 GET은 무조건 url에 붙여주는 식이죠.

    http://code.google.com/p/bs-network/source/browse/trunk/src/com/bsidesoft/BSnet/CloaderInfo.as

    이걸 보시면 충분히 이해하실듯.

  15. 좋은 내용 적어주셔서 감사해요. 네트워부분에 깊게 파고든적 없어서 이제 좀 알아야겠네요. ^^

  16. 저두 BitmapData 제한이 2880 픽셀인줄 알고있었는데요성능향상이 있었네요. 직접 테스트 해본 내용이 아니라서 라이브독 내용을 그대로 발췌해 봅니다.

    "AIR 1.5 및 Flash Player 10에서는 BitmapData 객체의 최대 크기가 8,192픽셀(폭 또는 높이)이며 총 픽셀 수는 16,777,216픽셀을 초과할 수 없습니다. 따라서 BitmapData 객체의 폭이 8,192픽셀이면 높이가 2,048픽셀 이하여야 합니다. Flash Player 9 이전 버전 및 AIR 1.1 이전 버전에서는 이 제한이 높이 2,880픽셀 및 폭 2,880픽셀입니다."

    예전엔 큰이미지는 잘게 잘라서 캡춰했었는데 많이 좋아진거겠죠 ^^ 정사각형으로 밨을때 2880px --> 4096px 이정도로 확장인가요 ㅎㅎ

    여기보면 꼼수로 크기제한 피하는 방법도 있네요
    http://flas3.wordpress.com/2008/05/30/bitmapdata-larger-than-2880-pixel/

  17. 좋은 정보 감사합니다. ^^ 링크걸어주신거 재미있는 내용이네요~~

  18. var bitmapData:BitmapData = new BitmapData(target.width, target.height, true, 0xFFFFFF);
    var drawingRectangle:Rectangle = new Rectangle(0, 0, target.width, target.height);
    bitmapData.draw(target, new Matrix(), null, null, drawingRectangle, false);

    추가로 이 소스에서 짜피 bitmapData의 가로 세로가 target에 수렴하는 경우는

    bitmapData.draw( target, null, null, null, bitmapData.rect, false);

    이렇게 줄일 수 있습니다. 자신의 사각영역을 나타내는 rect라는 속성이 있는지라 ^^;

  19. Blog Icon
    햇무리

    좋은 글 잘 봤습니다.
    그런데 제가 하려는건 반대의 경우인데요.
    서버에 web acess가 되지 않는 디렉토리에 jpg, png, gif 같은 이미지 파일이 있고,
    flex 에서 image 컨트롤에 그 이미지를 보여주고 싶어요.
    서버쪽에 download.php같은것을 만들고 image.source = "http://localhost/download.php?file=D:/filename.jpg";
    같이 하니 안되서요.
    이미지를 보안때문에 서버쪽에서 인증받고 보여주어야 해서요.
    어떻게 하면 될까요? 구글에서도 찾기가 쉽지 않네요. ^^

  20. 서버쪽에 인증을 받지 않는 이미지를 인증절차없이 flex에서 보여줄 수 있는 방법은 없겠죠? 브라우져에서 인증절차를 거치고 위처럼 접근해서 다운로드가 된다면 crossdomain 문제가 걸려있지 않는이상 flex에 이미지를 로드하는것은 문제가 없습니다.

  21. Blog Icon
    햇무리

    이미지 파일을 php에서 오픈해서 echo로 찍어주는데요.
    웹브라우져에서 url에 http://localhost/download.php?file=D:/filename.jpg 하면 이미지를 잘 불러오도록 php가 되어 있어요. 이미지는 서버에 있는 거에요. 두가지정도 만들어서 웹브라우져에서 직접 이미지를 보여주기도 하고 다운로드 창이 뜨기도 하지요.
    이렇게 php를 만들어 놓고, flex에서 image.source에 대입하면 이미지 컨트롤에 이미지가 안나와요.
    인증같은 것은 우선 빼고 하고 있는데요...
    구글를 좀 뒤져보니 BitmapData를 사용하는 것 같아서 지돌스타님 블로그까지 오게되었어요.
    방향은 좀 다르지만 비슷한것 같아서요. 하지만 잘 안되네요... ^^;
    지돌스타님 말씀은 그냥 잘 되어야 한다는 것 같은데 잘 안되네요...

  22. 지금의 말씀하신 결과로는 제가 어찌 답변하기가 곤란한 부분이 있습니다. Loader나 URLLoader로 로드한 결과물에 에러가 발생했는지 디버깅을 해보셨나요? 또 Flash Player는 debug버전을 설치하셔서 테스트 하시는 건가요? 이런 부분에 대해서 한번 해보시고 결과를 보셔서 구체적으로 질문해주시면 도움을 드릴 수 있지 않을까 생각합니다.

  23. Blog Icon
    햇무리

    간단한 소스를 하나 만들어봤어요.
    ===========================
    서버쪽 imgdown.php

    <?php

    $file = $_GET['file'];

    if (file_exists($file))
    {
    // Note: You should probably do some more checks
    // on the filetype, size, etc.
    $contents = file_get_contents($file);

    // Note: You should probably implement some kind
    // of check on filetype
    header('Content-type: image/jpeg');

    echo $contents;
    }
    ?>

    ===========================
    Flex 쪽

    <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">

    <mx:Script>
    <![CDATA[
    import mx.rpc.events.ResultEvent;
    public var req:URLRequest = new URLRequest("http://localhost/Imgdown.php");
    public var loader:URLLoader = new URLLoader();

    private function clickImage():void {
    var variables:URLVariables = new URLVariables();
    variables.file = "D:/Winter.jpg";
    req.data = variables;
    configureListeners(loader);
    loader.load(req);

    }
    private function configureListeners(dispatcher:IEventDispatcher):void {
    dispatcher.addEventListener(Event.COMPLETE, completeHandler);
    dispatcher.addEventListener(Event.OPEN, openHandler);
    dispatcher.addEventListener(ProgressEvent.PROGRESS, progressHandler);
    dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
    dispatcher.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);
    dispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
    }

    private function openHandler(event:Event):void {
    trace("openHandler: " + event);
    }

    private function progressHandler(event:ProgressEvent):void {
    trace("progressHandler loaded:" + event.bytesLoaded + " total: " + event.bytesTotal);
    }

    private function securityErrorHandler(event:SecurityErrorEvent):void {
    trace("securityErrorHandler: " + event);
    }

    private function httpStatusHandler(event:HTTPStatusEvent):void {
    trace("httpStatusHandler: " + event);
    }

    private function ioErrorHandler(event:IOErrorEvent):void {
    trace("ioErrorHandler: " + event);
    }

    private function onImageDownloadResult(event:ResultEvent):void {
    //testimg.source = (new Bitmap( event.result as BitmapData )) as Object;
    }

    private function completeHandler(event:Event):void {
    var loader:URLLoader = URLLoader(event.target);
    imgPic.source = loader.data;
    }
    ]]>
    </mx:Script>

    <mx:Image x="10" y="10" width="167" height="163" id="imgPic"/>
    <mx:Button x="10" y="181" label="이미지보기" id="btnImage" click="clickImage()"/>

    </mx:Application>

    ==========================

    버튼을 누르면 이미지 컨트롤에 이미지를 보여주게 하고 싶어요... 디버깅을 해보는데도 잘 모르겠네요...
    이미지는 D:/Winter.jpg 라고 넣어주시구요.
    웹브라우저에서 http://localhost/imgdown.php?file=D:/Winter.jpg 를 하면 잘 나와요.

    답변을 바로 주시니 정말 감사합니다... ^^

  24. 이렇게 소스를 보내주시는 것은 의미가 없습니당.
    제 테스트 환경은 php가 없는데 이것을 테스트 하기 위해 APM을 설치하는 것과 같으니 말이죠.

    말씀드렸듯이 각각 이벤트 핸들러 함수에 디버깅을 해보시고 어디서 문제가 있는지 확인해보세요. 그게 가장 빠른 방법입니다. 문제 없이 complete되었더라도 값이 제대로 들어오는지 체크도 하시구요.

  25. Blog Icon
    햇무리

    네, 알겠습니다.
    조금 더 해봐야겠네요... ^^ 감사합니다.

  26. Blog Icon
    김형광

    ByteArray를 POST 방식으로 서버측에 전송해주는 기능을 공부중인데..
    다음 처럼 간단하게 코딩하였습니다..

    protected function button1_clickHandler(event:MouseEvent):void
    {
    var loader:URLLoader = new URLLoader();
    var request:URLRequest =
    new URLRequest("http://localhost:8080/cLog.jsp");

    var bytes:ByteArray = new ByteArray();
    bytes.writeInt(12345);

    bytes.compress();
    bytes.position = 0;

    request.data = bytes;
    request.method = URLRequestMethod.POST;
    request.contentType = "application/octet-stream";

    loader.dataFormat = URLLoaderDataFormat.BINARY;
    loader.addEventListener(Event.COMPLETE, onCompleted);
    loader.addEventListener(IOErrorEvent.IO_ERROR, onError);

    loader.load(request);
    }

    근데.. Error #2032: 스트림 오류입니다... 라는 에러가 발생하는데.......
    혹시 도움이 되는 힌트 없을까요...?

  27. 이것만 가지고는 뭐가 문제인지는 완벽히 파악하기 어렵군요. 왜냐하면 그 원인은 상당히 많을겁니다.

    서버측 접속이 일단 되고 있는지 디버깅 해보시고 다른 브라우져에서도 실행해보세요. IE에서 문제가 있을 수 있습니다.



    일단 http://localhost:8080/cLog.jsp 이 접속이 되나요?

  28. 이제와 얘기지만 사실 이 포스트의 소스에 버그가 있다는..
    어진이가 이걸 긁어와서 테스트 시켜봤는데 뭐가 버그가 있어서 작동을 안했는데 가물가물..
    우리 CSloader건 잘 작동하지만 ㅋㅋㅋ

  29. 네~ 버그 있어요. 정확히 기억 안나는데... 어떤 한 부분입니다. 그부분은 저희도 수정해서 쓰고 있죠. ^^

  30. 이미지를 여러 사이즈로 변환해서 URLVariables 값과 같이 보내려고 하는데.
    한꺼번에 여러개의 이미지를 전송할 방법이 있는지요?
    아니면 하나씩 보내야 하는지요? 궁금합니다.

    지금 썸네일 이미지 Crop tool을 개발했는데 서버에 전송해야하는데 고민입니다.

  31. 어짜피 바이너리로 보낼 수 있으니 서버측/클라이언트측 프로토콜을 만드시기만 하면 될 것 같습니다.

  32. multipart/form-data으로 여러개의 이미지를 보낼수 있다는 이야기 인가요?

  33. byteArray로 묶어서 보내니 보내는 것 자체는 문제없습니다.
    하지만 분명한 것은 서버측에서도 받아들일 수 있는 프로토콜을 지원해줘야 한다는 겁니다. 클라이언트가 이렇게 보낼것이니 서버측에서는 이렇게 해석해서 각각 파일을 저장해줘라. 그리고 각각의 파일의 URL을 넘겨줘... 뭐 이런게 되어야 한다는 겁니다.

  34. Blog Icon
    정재훈

    멀티 업로드의 경우 위의 UploadPostHelper Class를 반복적으로사용해서 requestHeader에 추가 하면 된다는 거죠?
    혹은 UploadPostHelper.getPostData(file, byteArray, parameters);
    에서 byteArray부분은 array형태로 넘겨서 Header에 추가하면 되겠죠? 소스 분석해서 array형태로 멀티업로드가 가능하게 수정해도 되겠네요.

  35. --------------------------------------------------------------------------
    public static function getPostData(fileName:String, byteArray:ByteArray, parameters:Object = null):ByteArray {
    --------------------------------------------------------------------------
    byteArray:ByteArray를 Array()형태로 변경해서 다중파일이 가능하게 했습니다.
    감사합니다. 멋진소스입니다.

  36. 해결하셨군요. 도움이 되셨다니 기쁩니다. ^^

  37. Blog Icon
    담낭자

    좋은글 감사합니다.

  38. Blog Icon
    박진호

    지돌스타님 완젼 감사해요^^
    덕분에 두통이 날아갔습니다. 잠못 이루고 있었는데
    깔끔히 정리 돼버렸습니다.ㅎㅎ 좋은 하루 되세요!

  39. Blog Icon
    썬글쓰

    안녕하세요 지돌스타님

    위 내용을 읽고 적용하려는데 jsp로 바이트어레이를 받아서 처리하는 방법을 못찾겠습니다. ㅜㅜ

    간단한 팁이라도 부탁드려두 괜찮을까요?

  40. 음... html에서 File 첨부하는 것과 같은 겁니다. 그쪽 jsp 코드를 참고하세요.

  41. 좋은글 남겨주셔서 감사합니다!!!