diff --git a/Controller/UploadController.php b/Controller/UploadController.php
index 8aa8c7b260a58f8b91c182dabf62ee92e8ba5fd3..4a14530317e85c885e24e94ca64cbd2ed4069e32 100644
--- a/Controller/UploadController.php
+++ b/Controller/UploadController.php
@@ -102,43 +102,34 @@ class UploadController extends Controller
     {
         $this->validateToken($request);
 
-        $range = $request->headers->get('Content-Range');
-        $final = true;
-
-        if($range) {
+        if(null !== $range = $request->headers->get('Content-Range', null)) {
             $matches = [];
             if(!preg_match('@^bytes (\d+)-(\d+)/(\d+)$@', $range, $matches)) {
                 throw new BadRequestHttpException("Invalid Content-Range");
             }
             $start = intval($matches[1]);
+
             $end   = intval($matches[2]);
             $total = intval($matches[3]);
+            if($start < 0 || $start >= $end || $end >= $total) {
+                throw new HttpException(Response::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE);
+            }
 
-            $stream = $file->open('ab');
-            $stream->seek($start);
-
-            $final = $end === ($total-1);
-
+            $offset = $start;
+            $maxlen = 1 + ($end - $start);
+            $complete = $end === ($total-1);
         } else {
-            $stream = $file->open('wb');
+            $offset = 0;
+            $maxlen = -1;
+            $complete = true;
         }
 
         // Demande un filehandle plutôt que charger le contenu en mémoire
         $input = $request->getContent(true);
-
-        if(false !== $fileHandler = $stream->cast(STREAM_CAST_AS_STREAM)) {
-            // Utilise stream_copy_to_stream si le Gaufrette\Stream peut nous retourner un filehandle
-            stream_copy_to_stream($input, $fileHandler);
-        } else {
-            // Sinon fait une copie par blocs (moins performant)
-            while(!feof($input)) {
-                $stream->write(fread($input, 8192));
-            }
-        }
+        $copied = $file->copyFrom($input, $maxlen, $offset);
         fclose($input);
-        $stream->close();
 
-        if(!$final) {
+        if(!$complete) {
             return $this->createResponse(Response::HTTP_OK, 'Chunk received', ['range' => compact('start', 'end', 'total')]);
         }
 
diff --git a/Entity/UploadedFile.php b/Entity/UploadedFile.php
index d853fdfc203801c9b2a52263d5f32f400487b126..4d01b95e76258a7735bd6617ed80a3ca441c85e0 100644
--- a/Entity/UploadedFile.php
+++ b/Entity/UploadedFile.php
@@ -10,7 +10,6 @@ namespace Irstea\FileUploadBundle\Entity;
 use DateTime;
 use Doctrine\ORM\Mapping as ORM;
 use Gaufrette\Filesystem;
-use Gaufrette\Stream;
 use Gaufrette\StreamMode;
 use InvalidArgumentException;
 use Rhumsaa\Uuid\Uuid;
@@ -21,6 +20,9 @@ use Rhumsaa\Uuid\Uuid;
  */
 class UploadedFile
 {
+    // Taille de bloc utilisé pour les copies
+    static public $copyBlockSize = 8192;
+
     const ETAT_EN_COURS = 'en-cours';
     const ETAT_ORPHELIN = 'orphelin';
     const ETAT_NORMAL   = 'normal';
@@ -348,32 +350,140 @@ class UploadedFile
         return $this->getEtat() === self::ETAT_ORPHELIN;
     }
 
-    /**
+    /** Retourne la date de dernière modification dans le filesystem.
      *
-     * @param type $mode
-     * @return Stream
+     * @return \DateTime
      */
-    public function open($mode = 'rb')
+    public function getLastModified()
     {
-        $stream = $this->filesystem->createStream($this->getPath());
-        $stream->open(new StreamMode($mode));
-        return $stream;
+        return new \DateTime(sprintf('@%d', $this->filesystem->mtime($this->getPath())));
     }
 
-    /**
+    /** Retourne le contenu du fichier.
      *
-     * @return string
+     * @return string Une chaîne si $asResource vaut faux,
      */
     public function getContent()
     {
         return $this->filesystem->read($this->getPath());
     }
 
-    /**
-     * @return \DateTime
+    /** Ecrit dans le fichier.
+     *
+     * @param string $content
      */
-    public function getLastModified()
+    public function setContent($content)
     {
-        return new \DateTime(sprintf('@%d', $this->filesystem->mtime($this->getPath())));
+        return $this->filesystem->write($this->getPath(), $content, true);
+    }
+
+    /** Ecrit dans le fichier depuis un descripteur de fichier.
+     *
+     * @param resource $source
+     * @param int $maxlen
+     * @param int $writeOffset
+     * @return int
+     */
+    public function copyFrom($source, $maxlen = PHP_INT_MAX, $writeOffset = 0)
+    {
+        if($maxlen === 0) {
+            return 0;
+        }
+
+        $stream = $this->filesystem->createStream($this->getPath(), new StreamMode('cb'));
+        $stream->seek($writeOffset);
+
+        if(false !== $fileHandler = $stream->cast(STREAM_CAST_AS_STREAM)) {
+            // Utilise stream_copy_to_stream si le Gaufrette\Stream peut nous retourner un filehandle
+            $copied = $this->stream_copy_to_stream($source, $fileHandler, $maxlen);
+        } else {
+            // Sinon fait une copie par blocs (moins performant)
+            $copied = 0;
+            while(!$this->feof($source) && $copied <= $maxlen) {
+                $copied += $stream->write($this->fread($source, min(static::$copyBlockSize, $maxlen - $copied)));
+            }
+        }
+        $stream->close();
+
+        return $copied;
+    }
+
+    /** Envoie le contenu du fichier dan un descripteur de fichier.
+     *
+     * @param $dest $resource
+     * @param int $maxlen
+     * @param int $readOffset
+     * @return int
+     */
+    public function copyTo($dest, $maxlen = PHP_INT_MAX, $readOffset = 0)
+    {
+        $actualLength = min($maxlen, $this->getSize() - $readOffset);
+
+        if (0 <= $actualLength) {
+            return 0;
+        }
+
+        $stream = $this->filesystem->createStream($this->getPath(), new StreamMode('rb'));
+        $stream->seek($readOffset);
+
+        if(false !== $fileHandle = $stream->cast(STREAM_CAST_AS_STREAM)) {
+            // Utilise stream_copy_to_stream si le Stream nous renvoie un filehandle
+            $copied = $this->stream_copy_to_stream($fileHandle, $dest, $actualLength);
+        } else {
+            // Sinon, on fait ça à la main par blocs de 8ko
+            $copied = 0;
+            while(!$stream->eof() && $copied < $actualLength) {
+                $copied += $this->fwrite($dest, $stream->read(min(static::$copyBlockSize, $actualLength - $copied)));
+            }
+        }
+
+        $stream->close();
+
+        return $copied;
+    }
+
+    /** Wrapper de stream_copy_to_stream
+     *
+     * @param resource $source
+     * @param resource $dest
+     * @param int $maxlen
+     * @param int $offset
+     * @return int
+     */
+    protected function stream_copy_to_stream($source, $dest, $maxlen = -1, $offset = 0)
+    {
+        return stream_copy_to_stream($source, $dest, $maxlen, $offset);
+    }
+
+    /** Wrapper de feof
+     *
+     * @param resource $fh
+     * @return boolean
+     */
+    protected function feof($fh)
+    {
+        return feof($fh);
+    }
+
+    /** Wrapper de fread
+     *
+     * @param resource $fh
+     * @param int $maxlen
+     * @return int|boolean
+     */
+    protected function fread($fh, $maxlen = -1)
+    {
+        return fread($fh, $maxlen);
+    }
+
+    /** Wrapper de fwrite
+     *
+     * @param resource $fh
+     * @param int $maxlen
+     * @return int|boolean
+     */
+    protected function fwrite($fh, $maxlen = -1)
+    {
+        return fwrite($fh, $maxlen);
     }
 }
diff --git a/Http/UploadedFileResponse.php b/Http/UploadedFileResponse.php
index 034201517828ba5d363459d6892a4e849fa849d0..41402d212a7dad90b9ed6036678efdd49e4a1e91 100644
--- a/Http/UploadedFileResponse.php
+++ b/Http/UploadedFileResponse.php
@@ -203,24 +203,7 @@ class UploadedFileResponse extends Response
         }
 
         $out = fopen('php://output', 'wb');
-        $stream = $this->file->open('rb');
-
-        if(false !== $fileHandle = $stream->cast(STREAM_CAST_AS_STREAM)) {
-            // Utilise stream_copy_to_stream si le Stream nous renvoie un filehandle
-            stream_copy_to_stream($fileHandle, $out, $this->maxlen, $this->offset);
-        } else {
-            // Sinon, on fait ça à la main par blocs de 8ko
-            $stream->seek($this->offset);
-            for($i = floor($this->maxlen / 8192); $i > 0; $i--) {
-                fwrite($out, $stream->read(8192));
-            }
-            $remaining = $this->maxlen % 8192;
-            if($remaining > 0) {
-                fwrite($out, $stream->read($remaining));
-            }
-        }
-
-        $stream->close();
+        $this->file->copyTo($out, $this->maxlen, $this->offset);
         fclose($out);
     }