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.
The decision was to use the SOCKET. Using SOCKET you can send any type of HTTP requests and working with responses.
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 );
}