Search code examples
swiftfile-iobinaryfiles

Read large file of binary data in chunks of 1024 bytes


I'm trying to read an MP4 file in chunks of 1024 bytes. I've made a code that - almost - works. I'm doing the following:

let audioFilePath = Bundle.main.path(forResource: "video", ofType: "mp4")!
var chunks = [[UInt8]]()

  if let stream: InputStream = InputStream(fileAtPath: audioFilePath) {
    var buf: [UInt8] = [UInt8](repeating: 0, count: 1024)
    stream.open()
    while stream.hasBytesAvailable {
      stream.read(&buf, maxLength: 1024)
      chunks.append(buf)
    }
    stream.close()
  }

print(chunks.count)

The problem with the code above is that I'm reading an MP4 file of size 15.948.514 bytes. It means that it should finish in exactly 15.574 chunks (the last chunk may have less than 1024, but this is not a problem), but the code prints 15.576 chunks, and all of them of size 1024. What is wrong with the code above?


Solution

  • hasBytesAvailable can also return true if a read must be attempted in order to determine the availability of bytes. That is what happens in your case: The final read returns zero for “end of file.”

    hasBytesAvailable can be useful with input streams like TCP sockets to avoid a blocking read(), but is not really needed for reading from files. In any case, you must check the return value of read() which can be zero (end of file) or -1 (read error) or the actual number of bytes read into the buffer (which can be less than the number of bytes requested).

    Note also that you always append a chunk with 1024 bytes to the chunks array, even if the buffer is only partially filled with bytes from the input stream.

    if let stream = InputStream(fileAtPath: audioFilePath) {
        var buf = [UInt8](repeating: 0, count: 1024)
        stream.open()
    
        while case let amount = stream.read(&buf, maxLength: 1024), amount > 0 {
            // print(amount)
            chunks.append(Array(buf[..<amount]))
        }
        stream.close()
    }