/** * Delivers an image. * * @param string $hash * @param string $filename * @param boolean $thumbnail * @return Response */ public function getImage($hash = false, $filename = false, $thumbnail = false) { if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { header('HTTP/1.1 304 Not Modified'); die; } if ($hash !== false && $filename !== false) { $FileStorage = FileStorage::getHash($hash); $storagePath = !$thumbnail ? $FileStorage->getPath() : $FileStorage->getPathThumb(); $storagePathFull = !$thumbnail ? $FileStorage->getFullPath() : $FileStorage->getFullPathThumb(); $cacheTime = 315360000; /// 10 years if ($FileStorage instanceof FileStorage && Storage::exists($storagePath)) { $responseSize = Storage::size($storagePath); $responseHeaders = ['Cache-Control' => "public, max-age={$cacheTime}, pre-check={$cacheTime}", 'Expires' => gmdate(DATE_RFC1123, time() + $cacheTime), 'Last-Modified' => gmdate(DATE_RFC1123, File::lastModified($storagePathFull)), 'Content-Disposition' => "inline", 'Content-Length' => $responseSize, 'Content-Type' => $FileStorage->mime, 'Filename' => $filename]; $response = Response::stream(function () use($storagePathFull) { readfile($storagePathFull); }, 200, $responseHeaders); return $response; } } return abort(404); }
public function importInfinityPostAttachments(Post $model, Board &$board, $post) { $post_id = $model->post_id; if (!$post_id) { return 0; } $attachments = @json_decode($post->files, true); if (!is_array($attachments)) { return 0; } $aModels = []; foreach ($attachments as $aIndex => $attachment) { if (isset($attachment['error']) && $attachment['error']) { continue; } if (!isset($attachment['file_path']) || !isset($attachment['thumb_path'])) { continue; } $storage = null; $path = "{$this->targetLocation}/{$attachment['file_path']}"; $thumb = "{$this->targetLocation}/{$attachment['thumb_path']}"; if (file_exists($path)) { if (!isset($attachment['type'])) { continue; } if (!isset($attachment['hash'])) { $attachment['hash'] = md5(file_get_contents($path)); } $storage = FileStorage::getHash($attachment['hash']); if (!$storage || (!file_exists($storage->getFullPath()) || !file_exists($storage->getFullPathThumb()))) { $height = null; $width = null; if (isset($attachment['width']) && isset($attachment['height'])) { $height = $attachment['height']; $width = $attachment['width']; } if (!$storage) { $storage = new FileStorage(['hash' => $attachment['hash'], 'banned' => false, 'filesize' => $attachment['size'], 'file_width' => $width, 'file_height' => $height, 'mime' => $attachment['type'], 'meta' => null, 'first_uploaded_at' => Carbon::now(), 'last_uploaded_at' => Carbon::now(), 'upload_count' => 1]); } Storage::makeDirectory($storage->getDirectory()); if (!file_exists($storage->getFullPath())) { if (is_link($storage->getFullPath())) { unlink($storage->getFullPath()); } symlink($path, $storage->getFullPath()); } if ($attachment['thumbwidth'] && file_exists($thumb)) { $storage->has_thumbnail = true; $storage->thumbnail_width = $attachment['thumbwidth']; $storage->thumbnail_height = $attachment['thumbheight']; Storage::makeDirectory($storage->getDirectoryThumb()); if (!file_exists($storage->getFullPathThumb())) { if (is_link($storage->getFullPathThumb())) { unlink($storage->getFullPathThumb()); } symlink($thumb, $storage->getFullPathThumb()); } } $storage->save(); } if ($storage && $storage->exists) { $aModel = ['post_id' => $post_id, 'file_id' => $storage->file_id, 'filename' => $attachment['filename'], 'is_spoiler' => false, 'is_deleted' => false, 'position' => $aIndex]; $aModels[] = $aModel; } else { ++$skips; } } } FileAttachment::insert($aModels); return count($aModels); }
/** * Checks if a file exists. * * @param Request $request * @param Board $board * @return json */ public function getFile(Request $request, Board $board) { $hash = $request->get('md5'); $storage = FileStorage::getHash($hash); if (is_null($storage) || !$storage->hasFile()) { return [$hash => null]; } return [$hash => $storage]; }
/** * Delivers an image. * * @param \App\FileAttachment $attachment * @param string $filename * @param boolean $thumbnail * @return Response */ public function getImage($hash = false, $filename = false, $thumbnail = false) { if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { header('HTTP/1.1 304 Not Modified'); die; } $FileStorage = null; if (is_string($hash) && is_string($filename)) { $FileStorage = FileStorage::getHash($hash); } if ($FileStorage instanceof FileStorage) { $storagePath = !$thumbnail ? $FileStorage->getPath() : $FileStorage->getPathThumb(); $storagePathFull = !$thumbnail ? $FileStorage->getFullPath() : $FileStorage->getFullPathThumb(); $cacheTime = 31536000; /// 1 year if ($thumbnail && (!file_exists($storagePathFull) || is_dir($storagePathFull))) { if (is_dir($storagePathFull)) { unlink($storagePathFull); } $FileStorage->processThumb(); } if (is_link($storagePathFull)) { if (!is_readable($storagePathFull)) { abort(500, "Symlink file is unreadable."); } $storageExists = file_exists($storagePathFull); } else { $storageExists = Storage::exists($storagePath); } if ($storageExists) { ini_set("zlib.output_compression", "Off"); $responseSize = filesize($storagePathFull); $responseCode = 200; $responseHeaders = ['Cache-Control' => "public, max-age={$cacheTime}, pre-check={$cacheTime}", 'Expires' => gmdate(DATE_RFC1123, time() + $cacheTime), 'Last-Modified' => gmdate(DATE_RFC1123, File::lastModified($storagePathFull)), 'Content-Disposition' => Request::get('disposition', "inline"), 'Content-Length' => $responseSize, 'Content-Type' => $FileStorage->mime, 'Filename' => urldecode($filename)]; if ($thumbnail) { if ($FileStorage->isImage()) { $responseHeaders['Content-Type'] = Settings::get('attachmentThumbnailJpeg') ? "image/jpg" : "image/png"; } else { if ($FileStorage->isVideo()) { $responseHeaders['Content-Type'] = "image/jpg"; } else { if ($FileStorage->isAudio()) { $responseHeaders['Content-Type'] = "image/png"; } } } } // Determine if we can skip PHP content distribution. // This is hugely important. $xSendFile = false; // APACHE // Relies on the mod_xsendfile module. if (function_exists("apache_get_modules") && in_array("mod_xsendfile", apache_get_modules())) { $xSendFile = true; $responseHeaders['X-Sendfile'] = $storagePathFull; } else { if (preg_match("/nginx\\/1(\\.[0-9]+)+/", $_SERVER['SERVER_SOFTWARE'])) { $xSendFile = true; $responseHeaders['X-Accel-Redirect'] = "/{$storagePath}"; } else { if (preg_match("/lighttpd\\/1(\\.[0-9]+)+/", $_SERVER['SERVER_SOFTWARE'])) { $xSendFile = true; $responseHeaders['X-LIGHTTPD-send-file'] = $storagePathFull; } } } // Seek Audio and Video files. $responseStart = 0; $responseEnd = $responseSize - 1; if ($FileStorage->isVideo()) { $responseHeaders['Accept-Ranges'] = "0-" . ($responseSize - 1); if (isset($_SERVER['HTTP_RANGE'])) { list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2); if (strpos($range, ',') !== false) { return Response::make("Requested Range Not Satisfiable", 416, ['Content-Range' => "bytes {$responseStart}-{$responseEnd}/{$responseSize}"]); } if ($range == '-') { $responseStart = $this->size - substr($range, 1); } else { $range = explode('-', $range); $responseStart = $range[0]; $responseEnd = isset($range[1]) && is_numeric($range[1]) ? $range[1] : $responseEnd; } if ($responseStart > $responseEnd || $responseStart > $responseSize - 1 || $responseEnd >= $responseSize) { return Response::make("Requested Range Not Satisfiable", 416, ['Content-Range' => "bytes {$responseStart}-{$responseEnd}/{$responseSize}"]); } $responseCode = 206; $responseHeaders['Content-Length'] = $responseSize - $responseStart; $responseHeaders['Content-Range'] = "bytes {$responseStart}-{$responseEnd}/{$responseSize}"; unset($responseHeaders['Accept-Ranges']); unset($responseHeaders['Cache-Control']); unset($responseHeaders['Content-Disposition']); unset($responseHeaders['Expires']); } } // Are we using the webserver to send files? if ($xSendFile) { // Yes. // Send an empty 200 response with the headers. return Response::make("", $responseCode, $responseHeaders); } else { // No. // Get our hands dirty and stream the file. return Response::make(function () use($storagePathFull, $responseStart, $responseEnd) { if (!($responseStream = fopen($storagePathFull, 'rb'))) { abort(500, "Could not open requested file."); } if ($responseStart > 0) { fseek($responseStream, $responseStart); } $streamCurrent = 0; while (!feof($responseStream) && $streamCurrent < $responseEnd && connection_status() == 0) { echo fread($responseStream, min(1024 * 16, $responseEnd - $responseStart + 1)); $streamCurrent += 1024 * 16; } fclose($responseStream); }, $responseCode, $responseHeaders); } } } return abort(404); }
/** * Checks if a file exists. * * @param Request $request * @param Board $board * @return json */ public function getFile(Request $request, Board $board) { $hash = $request->get('md5'); $storage = FileStorage::getHash($hash); if (is_null($storage) || !$storage->hasFile()) { if (!is_null($storage)) { unlink($storage->getFullPath()); unlink($storage->getFullPathThumb()); } return [$hash => null]; } return [$hash => $storage]; }
/** * Delivers an image. * * @param string $hash * @param string $filename * @param boolean $thumbnail * @return Response */ public function getImage($hash = false, $filename = false, $thumbnail = false) { if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { header('HTTP/1.1 304 Not Modified'); die; } if ($hash !== false && $filename !== false) { $FileStorage = FileStorage::getHash($hash); $storagePath = !$thumbnail ? $FileStorage->getPath() : $FileStorage->getPathThumb(); $storagePathFull = !$thumbnail ? $FileStorage->getFullPath() : $FileStorage->getFullPathThumb(); $cacheTime = 315360000; /// 10 years if ($FileStorage instanceof FileStorage && Storage::exists($storagePath)) { $responseSize = Storage::size($storagePath); $responseCode = 200; $responseHeaders = ['Cache-Control' => "public, max-age={$cacheTime}, pre-check={$cacheTime}", 'Expires' => gmdate(DATE_RFC1123, time() + $cacheTime), 'Last-Modified' => gmdate(DATE_RFC1123, File::lastModified($storagePathFull)), 'Content-Disposition' => "inline", 'Content-Length' => $responseSize, 'Content-Type' => $FileStorage->mime, 'Filename' => $filename]; $responseStart = 0; $responseEnd = $responseSize - 1; if ($FileStorage->isVideo()) { $responseHeaders['Accept-Ranges'] = "0-" . ($responseSize - 1); if (isset($_SERVER['HTTP_RANGE'])) { list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2); if (strpos($range, ',') !== false) { return Response::make("Requested Range Not Satisfiable", 416, ['Content-Range' => "bytes {$responseStart}-{$responseEnd}/{$responseSize}"]); } if ($range == '-') { $responseStart = $this->size - substr($range, 1); } else { $range = explode('-', $range); $responseStart = $range[0]; $responseEnd = isset($range[1]) && is_numeric($range[1]) ? $range[1] : $responseEnd; } $responseEnd = $responseEnd > $responseEnd ? $responseEnd : $responseEnd; if ($responseStart > $responseEnd || $responseStart > $responseSize - 1 || $responseEnd >= $responseSize) { return Response::make("Requested Range Not Satisfiable", 416, ['Content-Range' => "bytes {$responseStart}-{$responseEnd}/{$responseSize}"]); } $responseCode = 206; $responseHeaders['Content-Length'] = $responseSize - $responseStart; $responseHeaders['Content-Range'] = "bytes {$responseStart}-{$responseEnd}/{$responseSize}"; unset($responseHeaders['Accept-Ranges']); unset($responseHeaders['Cache-Control']); unset($responseHeaders['Content-Disposition']); unset($responseHeaders['Expires']); } } return Response::stream(function () use($storagePathFull, $responseStart, $responseEnd) { if (!($responseStream = fopen($storagePathFull, 'rb'))) { abort(500, "Could not open requested file."); } if ($responseStart > 0) { fseek($responseStream, $responseStart); } $streamCurrent = 0; while (!feof($responseStream) && $streamCurrent < $responseEnd && connection_status() == 0) { echo fread($responseStream, min(1024 * 16, $responseEnd - $responseStart + 1)); $streamCurrent += 1024 * 16; } fclose($responseStream); }, $responseCode, $responseHeaders); } } return abort(404); }