$this->config = $container->getParameter($this->alias = RapsysPackBundle::getAlias());
 
                //Set ctx
-               $this->ctx = stream_context_create(
-                       [
-                               'http' => [
-                                       'max_redirects' => $_ENV['RAPSYSPACK_REDIRECT'] ?? 20,
-                                       'timeout' => $_ENV['RAPSYSPACK_TIMEOUT'] ?? (($timeout = ini_get('default_socket_timeout')) !== false && $timeout !== '' ? (float)$timeout : 60),
-                                       'user_agent' => $_ENV['RAPSYSPACK_AGENT'] ?? (($agent = ini_get('user_agent')) !== false && $agent !== '' ? (string)$agent : $this->alias.'/'.($this->version = RapsysPackBundle::getVersion()))
-                               ]
-                       ]
-               );
+               $this->ctx = stream_context_create($this->config['context']);
        }
 
        /**
         * @param Request $request The Request instance
         * @param string $hash The hash
         * @param string $equation The shorted equation
-        * @param int $width The width
         * @param int $height The height
+        * @param int $width The width
         * @return Response The rendered image
         */
-       public function captcha(Request $request, string $hash, string $equation, int $width, int $height, string $_format): Response {
+       public function captcha(Request $request, string $hash, string $equation, int $height, int $width, string $_format): Response {
                //Without matching hash
-               if ($hash !== $this->slugger->serialize([$equation, $width, $height])) {
+               if ($hash !== $this->slugger->serialize([$equation, $height, $width])) {
                        //Throw new exception
                        throw new NotFoundHttpException(sprintf('Unable to match captcha hash: %s', $hash));
                //Without valid format
                return $response;
        }
 
+       /**
+        * Return download file
+        *
+        * @param Request $request The Request instance
+        * @param string $hash The hash
+        * @param string $path The image path
+        * @return Response The rendered image
+        */
+       public function download(Request $request, string $hash, string $path/*, string $_format*/): Response {
+               //Without matching hash
+               if ($hash !== $this->slugger->hash($path)) {
+                       //Throw new exception
+                       throw new NotFoundHttpException('Invalid download hash');
+               //Without valid format
+               #} elseif ($_format !== 'jpeg' && $_format !== 'png' && $_format !== 'webp') {
+               #       //Throw new exception
+               #       throw new NotFoundHttpException('Invalid download format');
+               }
+
+               //Unshort path
+               $path = $this->slugger->unshort($short = $path);
+
+               //Without file
+               if (!is_file($path) || !($mtime = stat($path)['mtime'])) {
+                       //Throw new exception
+                       throw new NotFoundHttpException('Unable to get thumb file');
+               }
+
+               //Read thumb from cache
+               $response = new BinaryFileResponse($path);
+
+               //Set file name
+               $response->setContentDisposition(HeaderUtils::DISPOSITION_INLINE, basename($path));
+
+               //Set etag
+               //TODO: set etag to file content md5 ? cache it ?
+               $response->setEtag(md5($hash));
+
+               //Set last modified
+               $response->setLastModified(\DateTime::createFromFormat('U', strval($mtime)));
+
+               //Set as public
+               $response->setPublic();
+
+               //Return 304 response if not modified
+               $response->isNotModified($request);
+
+               //Return response
+               return $response;
+       }
+
        /**
         * Return facebook image
         *
         * @param Request $request The Request instance
         * @param string $hash The hash
         * @param string $path The image path
-        * @param int $width The width
         * @param int $height The height
+        * @param int $width The width
         * @return Response The rendered image
         */
-       public function facebook(Request $request, string $hash, string $path, int $width, int $height, string $_format): Response {
+       public function facebook(Request $request, string $hash, string $path, int $height, int $width, string $_format): Response {
                //Without matching hash
-               if ($hash !== $this->slugger->serialize([$path, $width, $height])) {
+               if ($hash !== $this->slugger->serialize([$path, $height, $width])) {
                        //Throw new exception
                        throw new NotFoundHttpException(sprintf('Unable to match facebook hash: %s', $hash));
                //Without matching format
                $response->setContentDisposition(HeaderUtils::DISPOSITION_INLINE, 'facebook-'.$hash.'.'.$_format);
 
                //Set etag
+               //TODO: set etag to file content md5 ? cache it ?
                $response->setEtag(md5($hash));
 
                //Set last modified
                $response->setContentDisposition(HeaderUtils::DISPOSITION_INLINE, basename($map));
 
                //Set etag
+               //TODO: set etag to file content md5 ? cache it ?
                $response->setEtag(md5(serialize([$height, $width, $zoom, $latitude, $longitude])));
 
                //Set last modified
                $response->setContentDisposition(HeaderUtils::DISPOSITION_INLINE, basename($map));
 
                //Set etag
+               //TODO: set etag to file content md5 ? cache it ?
                $response->setEtag(md5(serialize([$height, $width, $zoom, $coordinate])));
 
                //Set last modified
         * @param Request $request The Request instance
         * @param string $hash The hash
         * @param string $path The image path
-        * @param int $width The width
         * @param int $height The height
+        * @param int $width The width
         * @return Response The rendered image
         */
-       public function thumb(Request $request, string $hash, string $path, int $width, int $height, string $_format): Response {
+       public function thumb(Request $request, string $hash, string $path, int $height, int $width, string $_format): Response {
                //Without matching hash
-               if ($hash !== $this->slugger->serialize([$path, $width, $height])) {
+               if ($hash !== $this->slugger->serialize([$path, $height, $width])) {
                        //Throw new exception
                        throw new NotFoundHttpException('Invalid thumb hash');
                //Without valid format
                $path = $this->slugger->unshort($short = $path);
 
                //Set thumb
-               $thumb = $this->config['cache'].'/'.$this->config['prefixes']['thumb'].$path.'.'.$_format;
+               $thumb = $this->config['cache'].'/'.$this->config['prefixes']['thumb'].$path.'.'.$width.'x'.$height.'.'.$_format;
 
                //Without file
-               if (!is_file($path) || !($updated = stat($path)['mtime'])) {
+               if (!is_file($path) || !($mtime = stat($path)['mtime'])) {
                        //Throw new exception
                        throw new NotFoundHttpException('Unable to get thumb file');
                }
 
                //Without thumb up to date file
-               if (!is_file($thumb) || !($mtime = stat($thumb)['mtime']) || $mtime < $updated) {
+               if (!is_file($thumb) || !($updated = stat($thumb)['mtime']) || $updated < $mtime) {
                        //Without existing thumb path
                        if (!is_dir($dir = dirname($thumb))) {
                                //Create filesystem object
                                throw new \Exception(sprintf('Unable to write image "%s"', $thumb));
                        }
 
-                       //Set mtime
-                       $mtime = stat($thumb)['mtime'];
+                       //Set updated
+                       $updated = stat($thumb)['mtime'];
                }
 
                //Read thumb from cache
                $response->setContentDisposition(HeaderUtils::DISPOSITION_INLINE, 'thumb-'.$hash.'.'.$_format);
 
                //Set etag
+               //TODO: set etag to file content md5 ? cache it ?
                $response->setEtag(md5($hash));
 
                //Set last modified
-               $response->setLastModified(\DateTime::createFromFormat('U', strval($mtime)));
+               $response->setLastModified(\DateTime::createFromFormat('U', strval($updated)));
 
                //Set as public
                $response->setPublic();