Wysyłanie formularzy multipart/form-data

8 września 2009 – 14:08

Trafiłem ostatnio na dość dziwny problem. Potrzebowałem mianowicie wysłać z poziomu Flexa dane formularza, które po stronie serwera zostałyby odebrane przez PHP (lub inny język server-side).

Jednak cały kłopot polegał na tym, że:

  • dane te miały wykorzystać istniejący już kod obsługiwany przez formularz w HTML’u – czyli niejako miałem się podszyć pod zwykłą stronę www, nie wykorzystując żadnych udogodnień typu AMF, Webservice czy RemoteObject,
  • chciałem wysłać jednocześnie dane tekstowe (np. dane rejestracyjne) oraz binarne (np. obrazki JPG), bez limitu ilości,
  • obrazki nie były pobierane z dysku użytkownika przez FileReference.browse, ale tworzone w locie jako zrzuty komponentów przez ImageSnapshot i trzymane w pamięci w zmiennych typu ByteArray.

Jedyne co udało mi się znaleźć to artykuł na blogu Neer’a Friedman’a i jego projekt na Google Code. Był to dobry początek do zmian…
BTW: niejaki Mike Stead, którego komentarz widnieje pod wpisem Neer’a też zajmował się tym problemem i zdaje się, że nawet go jakoś elegancko rozwiązał, jednak jego stronka padła kompletnie i nie ma jej nawet w Google Cache…

Wywaliłem kod odpowiedzialny za używanie Socketów zamieniając go na zwykłego URLLoader’a oraz troche zrefaktoryzowałem kod (uporzadkowałem). Tym sposobem pojawiła się biblioteka MultipartRequestLoader :)

Przykładowa aplikacja (wraz z kodem źródłówym pod prawym klikiem myszki), a tutaj kod biblioteki: multipart_loader_imrahil.zip

Kod przykładowej aplikacji:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()" viewSourceURL="srcview/index.html">
    <mx:Script>
       <![CDATA[
           import multipart.MultipartVariables;
           import multipart.MultipartRequestLoader;
           
           private var loader:MultipartRequestLoader;

           private function init():void
           {
               loader = new MultipartRequestLoader('http://flex.imrahil.com/work/multipart_loader/upload.php');
               var variables:MultipartVariables = new MultipartVariables();
               var myByteArray:ByteArray = new ByteArray();

               // we will create a fake file content here, you should replace this with the binary data you want to upload
               for(var i:int=0; i<100;i++)
               {
                   myByteArray.writeUTFBytes("123by\nAR\r\nRA\x65\x0156");
               }

               variables.add('file_data', myByteArray, 'filename.bin');
               variables.add('another_var','extra data');

               loader.variables = variables;
               loader.addEventListener(Event.COMPLETE, fileUploaded);
               loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
               loader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
           }

           private function fileUploaded(event:Event):void
           {
               outputTxt.text += 'Result:';
               outputTxt.text += MultipartRequestLoader(event.target).responseBody;
           }
           private function securityErrorHandler(event:SecurityErrorEvent):void
           {
               outputTxt.text = "securityErrorHandler: " + event;
           }
           private function ioErrorHandler(event:IOErrorEvent):void
           {
               outputTxt.text = "ioErrorHandler: " + event;
           }
           private function upload(event:MouseEvent):void
           {
               loader.load();
           }
       ]]>
   </mx:Script>

    <mx:VBox width="100%" height="100%" horizontalAlign="center" verticalAlign="middle">
        <mx:Button label="Upload!" click="upload(event)" />
        <mx:TextArea id="outputTxt" width="500" height="250" />
    </mx:VBox>
</mx:Application>

Odpowiedz