2013-08-11 3 views
1

У меня есть стандартный сценарий AIR, скомпилированный на Flash или Flash Builder 4.5, предназначенный для загрузки файла на серверный код php. Код начинается с записи файла на рабочий стол пользователя перед завершением запроса.Ошибка # 2044: необработанный IOErrorEvent :. text = Ошибка # 2038: Ошибка ввода-вывода файла

import flash.net.*; 
import flash.filesystem.* 

//create the file 
var file:File = File.desktopDirectory; 
file = file.resolvePath("test.txt"); 
var fs:FileStream = new FileStream(); 
fs.open(file, FileMode.WRITE); 
//write "test" to txt file 
fs.writeUTFBytes("test"); 
fs.close(); 

//start request 
var req:URLRequest = new URLRequest(); 
req.method = "POST"; 
req.url = //php file 
var vars:URLVariables = new URLVariables(); 
vars.a = 1; 
vars.b = 2; 
vars.c = 3 
req.data = vars; 
file.upload(req, "txt_file"); 

Теперь, когда я загрузить на PHP файл на моем собственном тестовом сервере Apache на локальном хосте (XAMPP), загрузка файлов проходит без каких-либо ошибок. Однако, когда я пытаюсь загрузить на мой фактический сервер, я получаю сообщение об ошибке ввода/вывода:

Error #2044: Unhandled IOErrorEvent:. text=Error #2038: File I/O Error.

Я проверил мой MAX_FILE_SIZE набор в 2MB, так что это не проблема. URL-адрес также верен. Глядя на другие источники (например, http://www.judahfrangipane.com/blog/2007/01/01/error-2044-unhandled-ioerrorevent-texterror-2038-file-io-error/), некоторые предполагают, что проблема с Apache ModSecurity, выключил, установив .htaccess для каталога, чтобы:

SecFilterEngine Off 
SecFilterScanPOST Off 

Это не работает. Фактически, это делает остальную часть моего кода, предназначенного для получения данных с того же сервера устаревшими. Я также знаю, что это не междоменная проблема. Я также попытался установить PHP-код для возврата HTTP 200. Все еще не работает. Некоторые предположили, что эти ошибки немного случайны, и фактическая загрузка файла произошла независимо от ошибки. Таким образом, проблема может быть устранена, просто улавливая ошибку и игнорируя ее (http://dev.nuclearrooster.com/2008/04/05/uploading-with-a-filereference-from-flex-3-on-os-x/). Для меня это не вариант, так как мне нужно отслеживать ход загрузки.

Интересно, что это, по-видимому, проблема Mac OSX, поскольку у меня есть точный скрипт, работающий на компиляторе Windows без ошибок, хотя единственное условие - это то, что они были сделаны с более старой версией AIR. Поэтому я не уверен, так ли это здесь.

Я ударил головой о стену в течение 3 дней. Пожалуйста, помогите мне ......

UPDATE

Просто нашел, что мой сервер, где файл загрузки на возвращает ответ HTTP 301 по запросу на загрузку файлов, то, что снова происходит только на OSX на AIR, а не на Windows или данные формы, представленные через Firefox.

ответ

1

OK. Это определенно кажется проблемой Mac OSX. По какой-то причине HTTP-заголовки, созданные методом File.upload() в OSX, приводят к сбою внешнего сервера с помощью функции 301 Move Permently. Это НЕ происходит в Windows. Конечно, ничего, кроме ответа HTTP 2xx, приведет к ошибке ввода-вывода в отношении компилятора AIR. Поэтому, чтобы обойти проблему загрузки файлов, мы должны вручную создать заголовки HTTP, ответственные за загрузку per se. Я бы предложил не использовать File.upload() (или FileReference.upload()), пока эта проблема не будет решена.

Для этого файл, который мы загружаем, должен быть преобразован в ByteArray. Поэтому я бы использовал File/FileReference для захвата файла. Для фактической загрузки файл должен быть преобразован в ByteArray.

В моем примере выше, это очень просто:

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

var xml:XML = new XML(<items><item>one</item><item>two</item></items>); 
var data:ByteArray = new ByteArray(); 
data.writeUTF(xml.toXMLString()); 

Теперь, что касается создания вручную заголовки HTTP, Заслуга Джонатан Марстон (http://marstonstudio.com/2007/10/19/how-to-take-a-snapshot-of-a-flash-movie-and-automatically-upload-the-jpg-to-a-server-in-three-easy-steps/) и его прекрасной UploadPostHelper класса:

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; 
     } 

    } 
} 

Теперь мы используем класс для создания нашего HTTP-заголовка, используя старые добрые URLRequest:

var req:URLRequest = new URLRequest(); 
req.url = //server-side url (.php) 
req.contentType = "multipart/form-data; boundary=" + UploadPostHelper.getBoundary(); 
req.method = URLRequestMethod.POST; 
//Be sure to place the actual file name with its extension as the file name. Set the second argument as the ByteArray created earlier. Any other parameters (name-value pairs) can be added via an optional third parameter (see class above) 
req.data = UploadPostHelper.getPostData("test.xml", data); 
req.requestHeaders.push(new URLRequestHeader("Cache-Control", "no-cache")); 

Теперь вместо того, чтобы использовать File.upload() для выполнения запроса, просто использовать URLLoader:

var loader:URLLoader = new URLLoader(); 
//We have to load the data as binary as well 
loader.dataFormat = URLLoaderDataFormat.BINARY; 
loader.addEventListener(Event.COMPLETE, complete); 
loader.addEventListener(IOErrorEvent.IO_ERROR, ioerror); 
loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, secerror); 
loader.load(req); 

//Event handlers 
function complete(e:Event):void { 
    var ba:ByteArray = e.target.data; 
    //Returns the XML data 
    trace(ba.readUTF()); 
} 
function ioerror(e:IOErrorEvent):void {} 
function secerror(e:SecurityErrorEvent):void {} 

И все должно работать хорошо. Что касается серверной стороны, имя файла определяется классом выше как Filedata. Простой скрипт PHP для захвата файла будет выглядеть примерно так:

<?php 
echo file_get_contents($_FILES['Filedata']['tmp_name']); 
var_dump($_FILES['Filedata']); 
/* 
array(1) { 
    ["Filedata"]=>array(5) { 
     ["name"]=>string(8) "test.xml" 
     ["type"]=>string(24) "application/octet-stream" 
     ["tmp_name"]=>string(38) "root/tmp/php2jk8yk" 
     ["error"]=>int(0) 
     ["size"]=>int(58) 
    } 
} 
*/ 
?> 
+0

Единственная проблема с этим обходным путем заключается в том, что мы больше не можем отслеживать загрузку ProgressEvent, которая теперь отслеживает ход загрузки. – jazz090

Смежные вопросы