I am trying to load a 10mb chunk of a 600mb file in an Air desktop app. At first I tried loading the entire file however I got a "system out of memory" error, or the SWF would just quit without error.
I have not moved to reading only part of the file into a byte array, clearing the array and then reading the next part. This works better but it still gives memory issues towards the end of the file.
It is my understanding that the below method should only read the part of the file requested. But I think for some reason the file from the start to end of the chunk I want is actually read. My cut down code looks like this:
var file:File = File.desktopDirectory.resolvePath('input.txt');
var fileStream:FileStream = new FileStream();
fileStream.addEventListener(Event.COMPLETE, onLoaded);
fileStream.openAsync(file, FileMode.READ);
fileStream.position = _currentPos;
on load:
var fileStream:FileStream = event.target as FileStream;
_fileSize = fileStream.bytesAvailable;
bytes.clear();
fileStream.readBytes(bytes, 0, 10000000);
Then I do something with the bytes and call the above code again, this time the position is previous position plus length of the previous read.
When I examine the byte array I can see the size is equal to the start of the file to the end of the read. The byte array contains no data in elements before the start of the read, which is what I expected.
So am I only reading the chunk I want or the entire file?
If I am not can I do so?
How can I solve the memory issue?
The error I get is
Error: Error #1000: The system is out of memory.
at flash.filesystem::FileStream/readBytes()
EDIT:
After more testing I am sure the entire file is getting read into memory.
System.privateMemory reports memory usage just above the file size and when I inspect FileStream I see its the size of the file itself and not the chunk I want.
EDIT 2: I have now added readAhead and progress events, however its actually acting in the same way, except I control the read ahead size. Does readAhead actually stop the file from been read from start of file to start of read position?
Its as if fileStream.position is not really doing anything.
My new code looks like this:
private function read(pos:int):void {
_bytes.clear();
var file:File = File.desktopDirectory.resolvePath('big.mp4');
var fileStream:FileStream = new FileStream();
fileStream.readAhead = 10000000;
fileStream.addEventListener(ProgressEvent.PROGRESS, readProgressHandler)
fileStream.openAsync(file, FileMode.READ);
fileStream.position = _pos;
function readProgressHandler(event:ProgressEvent):void {
if (fileStream.bytesAvailable >= 10000000) {
fileStream.removeEventListener(ProgressEvent.PROGRESS, readProgressHandler)
fileStream.readBytes(_bytes, _pos, 10000000);
fileStream.close();
//get next bit
go();
}
}
}
private function go():void {
_pos = _pos + 10000000;
read(_pos);
}
If you want to read file asynchronously and don't want to load whole file you should set readAhead property.
The default value of this property is infinity: by default a file that is opened to read asynchronously reads as far as the end of the file.
Also note that COMPLETE
event is dispatched when whole file was read so you probably want to use PROGRESS
event instead
Signals the availability of new data on the stream.
And don't think about FileStream
as kind of ByteArray
because it is not. It uses the same data input/ouput interaface but the data you read from it is not stored in RAM - you simply read data from hard drive.
ByteArray
for example has length
property which is not property of file stream.
You could also find This demonstration useful to you.
Edit
The whole purpose of asynchronous read is get part of file on each progress event listener call so that you don't open and close the file over and over again. Below example read 3MB from a file. You can start reading at any position you want.
var _pos:Number = 100000; //starting positon - could be any within a file.
var bytes:ByteArray = new ByteArray();
var totalToRead:Number = 3 * 1024 * 1024;
var file:File = File.desktopDirectory.resolvePath("SILENCE GROOVE - live at Kompas Audio .mp3");
var fileStream:FileStream = new FileStream();
fileStream.readAhead = 10000000;
fileStream.addEventListener(ProgressEvent.PROGRESS, readProgressHandler);
fileStream.openAsync(file, FileMode.READ);
fileStream.position = _pos;
function readProgressHandler(event:ProgressEvent):void {
var tr:Number = Math.min(totalToRead - bytes.length , fileStream.bytesAvailable); //bytes to read.
fileStream.readBytes(bytes, bytes.length, tr);
if (bytes.length < totalToRead) return;
fileStream.removeEventListener(ProgressEvent.PROGRESS, readProgressHandler);
fileStream.close();
trace(bytes.length / (1024 * 1024), "MB readed.");
}
More details:
And to make sure things are clear. You don't need to take any additional steps of splitting this to 10MB chunks. You can set totalToRead to 700MB and read data of the file at any time you want (as soon as it is available). The readAhead
property just tells how many bytes should be loaded into memory even if you don't read them immediately. You would probably get less of data at each ProgressEvent
but the events will be dispatcher until you hit the readAhead
value and then the file read sold stop unit your read already loaded data.