Products
Technologies

Developer resources

Resume download in AIR via SOCKET

Avg. Rating 4.2

Problem

The task was to implement a download manager with resume download support. Because of security policy we cant add and change HTTP headers (in this case Range: header) we cand complete this task with standard set of classes.

Solution

The decision was to use the SOCKET. Using SOCKET you can send any type of HTTP requests and working with responses.

Detailed explanation

Here is a some set of source code which is demonstrace how to use SOCKET to implement resume download and how to write received data. Sample class is in attached archive.

_socket.addEventListener( Event.CLOSE, socketClose ); // === COMPLETE
_socket.addEventListener( Event.CONNECT, socketConnect );
_socket.addEventListener( IOErrorEvent.IO_ERROR, uStreamIOErrorHandler );
_socket.addEventListener( SecurityErrorEvent.SECURITY_ERROR, uStreamSecurityErrorHandler );
_socket.addEventListener( ProgressEvent.SOCKET_DATA, socketData ); // === PROGRESS

public function downloadFile ( $fileURL:String, $fileName:String, $pathToSave:String ) : void
{
    _isBusy            = true;
   
    // _pathToTarget is path to file on server
    // i.e. /getFile?myfile.jpg
    // NOTE it cant be an url or anything, depends on serve config
    _pathToTarget    = "/getFile?" + $fileURL.split( "/getFile?")[1];
    _host            = URLUtil.getServerName( $fileURL );

    var fl : File = new File().resolvePath( $pathToSave + File.separator + $fileName );

    // PATH TO YOU DOWNLOAD FILE
    var fl : File = new File().resolvePath( $pathToSave + File.separator + $fileName );

    if ( fl.exists )
    {
        // position from download will be started
        _downloadFromBytes    = fl.size;
       
        // indicates is headers from socket data wah removed or not
        _headerRemoved        = false;
       
        // creating file stream to append downloaded data
        _fileStream = new FileStream();
        _fileStream.open( fl, FileMode.APPEND );

        // check is socket is connected already
        if ( _socket.connected )
        {
            sendRequest();
        }
        else
        {
            // connecting to your host and port
            // i.e. host http://www.mydomain.com
            // i.e. post 80
            _socket.connect( _host, _port );
        }
    }
    else
    {
        // if file is not exist downloading file with bacis download method.
        _fileStream = new FileStream();
        _urlRequest = new URLRequest( $fileURL );

        _fileStream.open( fl, FileMode.WRITE );
        _urlStream.load( _urlRequest );
    }
}


private function socketConnect ( event:Event ) : void
{
    sendRequest();
}

 private function socketClose ( event:Event ) : void
{
    _fileStream.close();
}

 private function uStreamIOErrorHandler ( event:IOErrorEvent ) : void
{

    _fileStream.close();
}

 private function uStreamSecurityErrorHandler ( event:SecurityErrorEvent ) : void
{
    _fileStream.close();
}

 private function sendRequest () : void
{
    var request : String = "";
   
    // _pathToTarget is path to file on server
    // i.e. /getFile?myfile.jpg
    // NOTE it cant be an url or anything, depends on serve config
    request += "GET " + _pathToTarget + " HTTP/1.1" + "\n"
   
    // host where file is hosted
    // i.e. http://www.mydomain.com
    request += "Host: " + _host + "\n";
   
    // bytes from download will be started
    request += "Range: bytes=" + _downloadFromBytes + "- ";
    request += "\n\n";

    // sending this request
    sendMultiByte( request );
}


private function socketData ( event:ProgressEvent ) : void
{
    // creating holder for loaded bytes
    var _loadedBytes        : ByteArray = new ByteArray();
   
    // length of content to download from server
    var _contentLength        : Number = 0;
   
    // creating holder for bytes to APPEND to existed file on you HDD
    var _bytesToWrite        : ByteArray = new ByteArray();

    // if we have some data from socket
    if ( _socket.bytesAvailable )
    {
        // reading bytes
        _socket.readBytes( _loadedBytes, _prevBytesPosition );

        // checking if headers was removed
        // if not we must remove HTTP headers from socket data,
        // to avoid writing this HTTP headers in to file body on HDD
        if ( !_headerRemoved )
        {
            var loadedChars : String = "";
           
            // making loop to remove all HTTP headers
            for ( var i : int = 0; i < _loadedBytes.length; i++  )
            {
                if ( loadedChars.indexOf( "13101310" ) > -1 || loadedChars.indexOf( "1313" ) > -1 )
                {
                    _bytesToWrite.writeByte( _loadedBytes[ i ] );
                }
                else
                {
                    loadedChars += String( _loadedBytes[i] );
                }
            }
           
            var str                : String = _loadedBytes.toString();
            var httpStatus        : String;
            var headerEndIndex    : int = str.indexOf( "Content-Transfer-Encoding: binary\r\n\r\n" );

            // getting content length, it may be useful for progress calculation
            var _range            : String = StringUtil.trim( str.substr( str.indexOf( "Content-Range: bytes " ) + 21, str.indexOf( "Connection:" ) - ( str.indexOf( "Content-Range: bytes " ) + 21 ) ) )
            _contentLength = parseInt( _range.substr( _range.lastIndexOf( "/" ) + 1 ) );

            // setting headerRemoved flag to true, to avoid next time checking for "clear" content
            _headerRemoved = true;
        }
        else
        {
            _bytesToWrite = _loadedBytes;
        }
       
        // writing file data in to file on HDD
        _fileStream.writeBytes( _bytesToWrite );
    }

    _prevBytesPosition = _socket.bytesAvailable;

    event.bytesTotal = _contentLength;

    dispatchEvent( event );
}

Report abuse

Related recipes