diff --git a/include/class.file.php b/include/class.file.php
index d0404f40679904778be6d8eb16532a56b6b8eb4e..d68a3b11b1f403a4a0aacc1a58db37c61151eb68 100644
--- a/include/class.file.php
+++ b/include/class.file.php
@@ -394,7 +394,7 @@ class AttachmentFile {
         // TODO: Make this resumable so that if the file cannot be migrated
         //      in the max_execution_time, the migration can be continued
         //      the next time the cron runs
-        while ($block = $source->read()) {
+        while ($block = $source->read($target->getBlockSize())) {
             hash_update($before, $block);
             $target->write($block);
         }
@@ -561,6 +561,7 @@ class FileStorageBackend {
     var $meta;
     static $desc = false;
     static $registry;
+    static $blocksize = 131072;
 
     /**
      * All storage backends should call this function during the request
@@ -604,6 +605,14 @@ class FileStorageBackend {
         return new $class($file);
     }
 
+    /**
+     * Returns the optimal block size for the backend. When migrating, this
+     * size blocks would be best for sending to the ::write() method
+     */
+    function getBlockSize() {
+        return static::$blocksize;
+    }
+
     /**
      * Create an instance of the storage backend linking the related file.
      * Information about the file metadata is accessible via the received
@@ -712,10 +721,12 @@ class FileStorageBackend {
 define('CHUNK_SIZE', 500*1024); # Beware if you change this...
 class AttachmentChunkedData extends FileStorageBackend {
     static $desc = "In the database";
+    static $blocksize = CHUNK_SIZE;
 
     function __construct($file) {
         $this->file = $file;
-        $this->_pos = 0;
+        $this->_chunk = 0;
+        $this->_buffer = false;
     }
 
     function length() {
@@ -725,12 +736,19 @@ class AttachmentChunkedData extends FileStorageBackend {
         return $length;
     }
 
-    function read() {
+    function read($amount=CHUNK_SIZE, $offset=0) {
         # Read requested length of data from attachment chunks
-        list($buffer) = @db_fetch_row(db_query(
-            'SELECT filedata FROM '.FILE_CHUNK_TABLE.' WHERE file_id='
-            .db_input($this->file->getId()).' AND chunk_id='.$this->_pos++));
-        return $buffer;
+        while (strlen($this->_buffer) < $amount + $offset) {
+            list($buf) = @db_fetch_row(db_query(
+                'SELECT filedata FROM '.FILE_CHUNK_TABLE.' WHERE file_id='
+                .db_input($this->file->getId()).' AND chunk_id='.$this->_chunk++));
+            if (!$buf)
+                break;
+            $this->_buffer .= $buf;
+        }
+        $chunk = substr($this->_buffer, $offset, $amount);
+        $this->_buffer = substr($this->_buffer, $offset + $amount);
+        return $chunk;
     }
 
     function write($what, $chunk_size=CHUNK_SIZE) {
@@ -740,12 +758,12 @@ class AttachmentChunkedData extends FileStorageBackend {
             if (!$block) break;
             if (!db_query('REPLACE INTO '.FILE_CHUNK_TABLE
                     .' SET filedata=0x'.bin2hex($block).', file_id='
-                    .db_input($this->file->getId()).', chunk_id='.db_input($this->_pos++)))
+                    .db_input($this->file->getId()).', chunk_id='.db_input($this->_chunk++)))
                 return false;
             $offset += strlen($block);
         }
 
-        return $this->_pos;
+        return $this->_chunk;
     }
 
     function unlink() {