Created
January 11, 2014 02:27
-
-
Save 7interactivestudio/8366228 to your computer and use it in GitHub Desktop.
ProgressiveDownload allows to download a file by chunks of data (useful for mobile devices, and memory friendly) and has the ability to pause/resume downloads saving download information for later usage. You can close the app ,and resume the download whenever you want.
The API needs to be polish a lot... this is an "alpha version".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Code by rodrigolopezpeker (aka 7interactive™) on 1/9/14 12:30 PM. | |
*/ | |
package rodrigolopezpeker.net { | |
import flash.events.Event; | |
import flash.events.ProgressEvent; | |
import flash.filesystem.File; | |
import flash.filesystem.FileMode; | |
import flash.filesystem.FileStream; | |
import flash.net.SharedObject; | |
import flash.net.URLRequest; | |
import flash.net.URLRequestHeader; | |
import flash.net.URLStream; | |
import flash.utils.ByteArray; | |
/** | |
* | |
* Basic usage: | |
* ------------------------------------------------ | |
* | |
* var remoteFileURL:String = "http://airdownload.adobe.com/air/mac/download/latest/AIRSDK_Compiler.tbz2" ; | |
* var pd:ProgressiveDownload = new ProgressiveDownload() ; | |
* pd.targetFile = File.desktop.resolvePath('airsdk.tbz2'); | |
* pd.onComplete = onDownloadComplete ; | |
* pd.onProgress = onDownloadProgress ; | |
* pd.load( remoteFileURL ) ; | |
* | |
* function onDownloadProgress(loadedBytes:uint, totalBytes:uint):void { | |
* trace("download progress: " + Math.round( loadedBytes/totalBytes * 100 ) "%" ) ; | |
* } | |
* function onDownloadComplete():void { | |
* trace("file downloaded complete"); | |
* } | |
* | |
* // somewhere in the code. | |
* | |
* pd.pause() ; // pause the download | |
* pd.resume() ; // resume the download | |
* pd.reset() ; // reset the instances, stops download, and delete the File references (not the actual file). | |
* | |
* pd.saveToSO() ; // saves in cache the file paths, url and bytes positions for later usage (useful for persistent reference) | |
* pd.usePendingFile() ;// sets the instance to the SharedObject data stored by saveToSO(), just call pd.load() to resume. | |
* pd.clearSO() ; // clear the SharedObject data (if any). | |
* | |
*/ | |
public class ProgressiveDownload { | |
public static const SHARED_OBJECT_ID:String = '_resumableDownloads' ; | |
public static var verbose:Boolean = true ; | |
private var rangeHeader:URLRequestHeader; | |
private var fromBytePos:int; | |
public var totalBytes:int; | |
public var loadedBytes:uint; | |
public var request:URLRequest; | |
public var urlStream:URLStream; | |
public var fileStream:FileStream; | |
private var _currentBytesChunk:ByteArray ; | |
// current file downloaded. | |
private var _targetFile:File; | |
public var complete:Boolean; | |
public var paused:Boolean; | |
public var onComplete:Function; | |
public var onProgress:Function; | |
// used to resume downloads | |
private var _so:SharedObject; | |
private var usingPending:Boolean; | |
public function ProgressiveDownload() { | |
_so = SharedObject.getLocal( SHARED_OBJECT_ID ) ; | |
complete = false ; | |
rangeHeader = new URLRequestHeader("range"); | |
request = new URLRequest(); | |
request.requestHeaders.push(rangeHeader); | |
urlStream = new URLStream(); | |
fileStream = new FileStream(); | |
usingPending = false ; | |
_currentBytesChunk = new ByteArray(); | |
} | |
public function load(pURL:String=null):void { | |
if(pURL) | |
request.url = pURL ; | |
complete = false ; | |
paused = usingPending ; | |
if( !usingPending ) | |
loadedBytes = totalBytes = 0 ; | |
updateRangeHeader() ; | |
listeners(true); | |
urlStream.load(request); | |
} | |
private function progressHandler( e:ProgressEvent ):void { | |
// first time only... | |
if( loadedBytes == 0 ){ | |
totalBytes = e.bytesTotal ; | |
} | |
// loadedBytes = e.bytesLoaded ; | |
writeDataToDisk(); | |
if(onProgress!=null) | |
onProgress( loadedBytes, totalBytes ); | |
} | |
private function completeHandler( e:Event ):void { | |
writeDataToDisk(); | |
// clear the cache. | |
clearSO(); | |
usingPending = false ; | |
complete = true ; | |
paused = false ; | |
if(onComplete!=null) onComplete(); | |
listeners(false); | |
} | |
private function listeners(b:Boolean):void { | |
if( b ){ | |
urlStream.addEventListener( ProgressEvent.PROGRESS, progressHandler ); | |
urlStream.addEventListener( Event.COMPLETE, completeHandler ); | |
} else { | |
urlStream.removeEventListener( ProgressEvent.PROGRESS, progressHandler ); | |
urlStream.removeEventListener( Event.COMPLETE, completeHandler ); | |
} | |
} | |
private function writeDataToDisk():void { | |
_currentBytesChunk.clear(); | |
urlStream.readBytes(_currentBytesChunk); | |
loadedBytes += _currentBytesChunk.length ; | |
fileStream.open( _targetFile, FileMode.APPEND ); | |
fileStream.writeBytes(_currentBytesChunk); | |
fileStream.close(); | |
} | |
private function updateRangeHeader():void { | |
rangeHeader.value = "bytes="+fromBytePos+"-" ; | |
if( totalBytes > 0 ) rangeHeader.value += totalBytes ; | |
} | |
public function get running():Boolean { | |
return urlStream.connected ; | |
} | |
public function pause():void { | |
if(complete){ | |
if( verbose ) | |
trace('ProgressiveDownload::pause() > Download already complete.'); | |
return ; | |
} | |
fromBytePos = loadedBytes ; | |
paused = true ; | |
listeners(false); | |
if( urlStream.connected ) urlStream.close() ; | |
} | |
public function resume():void { | |
if(complete || urlStream.connected ){ | |
if( verbose ) | |
trace('ProgressiveDownload::resume() > File already downloaded'); | |
return ; | |
} | |
paused = false ; | |
updateRangeHeader() ; | |
listeners(true); | |
urlStream.load(request); | |
} | |
public function get targetFile():File {return _targetFile;} | |
public function set targetFile(value:File):void { | |
_targetFile = value; | |
} | |
public function reset():void { | |
_targetFile = null ; | |
_currentBytesChunk.clear(); | |
listeners(false); | |
totalBytes = 0 ; | |
fromBytePos = 0 ; | |
loadedBytes = 0 ; | |
if( urlStream.connected ) urlStream.close() ; | |
complete = false ; | |
paused = false ; | |
} | |
public function clearSO():void { | |
_so.clear(); | |
} | |
/*public function hasPendingFile(): Boolean { | |
return _so.data.hasOwnProperty('requestURL'); | |
}*/ | |
public function usePendingFile(): Boolean { | |
if( !_so.data.hasOwnProperty('requestURL') || running ) return false ; | |
var file:File = new File( _so.data.localFileURL ) ; | |
if(!file.exists) { | |
if( verbose ) | |
trace('ProgressiveDownload::usePendingFile() > the local file doesn\'t exists.'); | |
return false ; | |
} | |
request.url = _so.data.requestURL ; | |
_targetFile = file ; | |
fromBytePos = loadedBytes = _so.data.bytesLoaded ; | |
totalBytes = _so.data.bytesTotal ; | |
usingPending = true ; | |
// paused state | |
return true ; | |
} | |
public function getPendingFileInfo():Object { | |
return _so.data ; | |
} | |
public function saveToSO():void { | |
// useful to resume the download in the future. | |
if( !request.url ){ | |
if( verbose ) | |
trace('ProgressiveDownload::saveDownloadProgress() > needs a target url to download.'); | |
return ; | |
} | |
if( !_targetFile ){ | |
if( verbose ) | |
trace('ProgressiveDownload::saveDownloadProgress() > needs an output file to save the path reference.'); | |
return ; | |
} | |
if( totalBytes == 0 ){ | |
if( verbose ) | |
trace('ProgressiveDownload::saveDownloadProgress() > no data has been saved yet.'); | |
return ; | |
} | |
_so.data.requestURL = request.url ; | |
_so.data.localFileURL = _targetFile.nativePath ; | |
_so.data.bytesLoaded = loadedBytes ; | |
_so.data.bytesTotal = totalBytes ; | |
_so.flush(); | |
} | |
public function setURL(pText:String):void { | |
request.url = pText ; | |
} | |
public function get currentBytesChunk():ByteArray { | |
return _currentBytesChunk; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment