ich geschaffen habe MediaFileDownload.php (von MediaFileUpload.php in Google api für php)
<?php
/**
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Uri;
use Psr\Http\Message\RequestInterface;
/**
* Manage large file downloads, which may be media but can be any type
* of sizable data.
*/
class Google_Http_MediaFileDownload
{
const DOWNLOAD_MEDIA_TYPE = 'media';
const DOWNLOAD_MULTIPART_TYPE = 'multipart';
const DOWNLOAD_RESUMABLE_TYPE = 'resumable';
/** @var string $mimeType */
private $mimeType;
/** @var string $data */
private $data;
/** @var bool $resumable */
private $resumable;
/** @var int $chunkSize */
private $chunkSize;
/** @var int $size */
private $size;
/** @var string $resumeUri */
private $resumeUri;
/** @var int $progress */
private $progress;
/** @var Google_Client */
private $client;
private $logger;
/** @var Psr\Http\Message\RequestInterface */
private $request;
/** @var string */
private $boundary;
/**
* Result code from last HTTP call
* @var int
*/
private $httpResultCode;
/**
* @param $mimeType string
* @param $data string The bytes you want to download.
* @param $resumable bool
* @param bool $chunkSize File will be downloaded in chunks of this many bytes.
* only used if resumable=True
*/
public function __construct(
Google_Client $client,
RequestInterface $request,
$mimeType,
$data,
$logger = null,
$resumable = false,
$chunkSize = false
) {
$this->logger = $logger;
$this->client = $client;
$this->request = $request;
$this->mimeType = $mimeType;
$this->data = $data;
$this->resumable = $resumable;
$this->chunkSize = $chunkSize;
$this->progress = 0;
//$this->process();
}
/**
* Set the size of the file that is being downloaded.
* @param $size - int file size in bytes
*/
public function setFileSize($size)
{
$this->size = $size;
}
/**
* Return the progress on the download
* @return int progress in bytes downloaded.
*/
public function getProgress()
{
return $this->progress;
}
/**
* Send the next part of the file to download.
* @param [$chunk] the next set of bytes to send. If false will used $data passed
* at construct time.
*/
public function nextChunk($chunk = false)
{
//$resumeUri = $this->getResumeUri();
// if (false == $chunk) {
// $chunk = substr($this->data, $this->progress, $this->chunkSize);
// }
// $lastBytePos = $this->progress + strlen($chunk) - 1;
// $headers = array(
// 'content-range' => "bytes $this->progress-$lastBytePos/$this->size",
// 'content-length' => strlen($chunk),
// 'expect' => '',
// );
// $request = new Request(
// 'PUT',
// $resumeUri,
// $headers,
// Psr7\stream_for($chunk)
// );
//TODO FV calculate start end range from response if present ????
$resumeUri = $this->getResumeUri();
$lastBytePos = $this->progress + $this->chunkSize - 1;
$lastBytePosSize = $this->size - 1;
$lastBytePos = min($lastBytePos,$lastBytePosSize);
$headers = array('Range' => "bytes=$this->progress-$lastBytePos");
$this->logger->info("'Range' [" . "bytes=$this->progress-$lastBytePos" . "]");
$this->logger->info("resumeUri [" . $resumeUri . "]");
$request = new Request(
'GET',
$resumeUri,
$headers
);
return $this->makeGetRequest($request);
}
/**
* Return the HTTP result code from the last call made.
* @return int code
*/
public function getHttpResultCode()
{
return $this->httpResultCode;
}
/**
* Sends a PUT-Request to google drive and parses the response,
* setting the appropiate variables from the response()
*
* @param Google_Http_Request $httpRequest the Reuqest which will be send
*
* @return false|mixed false when the download is unfinished or the decoded http response
*
*/
private function makePutRequest(RequestInterface $request)
{
$response = $this->client->execute($request);
$this->httpResultCode = $response->getStatusCode();
if (308 == $this->httpResultCode) {
// Track the amount downloaded.
$range = explode('-', $response->getHeaderLine('range'));
$this->progress = $range[1] + 1;
// Allow for changing download URLs.
$location = $response->getHeaderLine('location');
if ($location) {
$this->resumeUri = $location;
}
// No problems, but download not complete.
return false;
}
return Google_Http_REST::decodeHttpResponse($response, $this->request);
}
private function makeGetRequest(RequestInterface $request)
{
$response = $this->client->execute($request);
$this->httpResultCode = $response->getStatusCode();
$this->logger->info("httpResultCode [" . $this->httpResultCode . "]");
//if (300 == $this->httpResultCode || 200 == $this->httpResultCode) {
if ($this->httpResultCode >= 200 && $this->httpResultCode < 300) {
// Track the amount downloaded.
// $range = explode('-', $response->getHeaderLine('range'));
// $this->progress = $range[1] + 1;
$range = explode('-', $response->getHeaderLine('content-range'));
$this->logger->info("range[0] [" . $range[0] . "]");
$this->logger->info("range[1] [" . $range[1] . "]");
$range = explode('/', $range[1]);
$this->logger->info("range[0] [" . $range[0] . "]");
$this->progress = $range[0] + 1;
$this->logger->info("progress [" . $this->progress . "]");
// Allow for changing download URLs.
$location = $response->getHeaderLine('location');
if ($location) {
$this->resumeUri = $location;
$this->logger->info("resumeUri from location [" . $this->resumeUri . "]");
}
// No problems, but download not complete.
//return false;
return $response;
}
else if($this->httpResultCode >= 400)
{
return Google_Http_REST::decodeHttpResponse($response, $this->request);
}
return false;
//return Google_Http_REST::decodeHttpResponse($response, $this->request);
}
/**
* Resume a previously unfinished download
* @param $resumeUri the resume-URI of the unfinished, resumable download.
*/
public function resume($resumeUri)
{
$this->resumeUri = $resumeUri;
$headers = array(
'content-range' => "bytes */$this->size",
'content-length' => 0,
);
$httpRequest = new Request(
'PUT',
$this->resumeUri,
$headers
);
return $this->makePutRequest($httpRequest);
}
/**
* @return Psr\Http\Message\RequestInterface $request
* @visible for testing
*/
private function process()
{
$this->transformToDownloadUrl();
$request = $this->request;
$postBody = '';
$contentType = false;
$meta = (string) $request->getBody();
$meta = is_string($meta) ? json_decode($meta, true) : $meta;
$downloadType = $this->getDownloadType($meta);
$request = $request->withUri(
Uri::withQueryValue($request->getUri(), 'downloadType', $downloadType)
);
$mimeType = $this->mimeType ?
$this->mimeType :
$request->getHeaderLine('content-type');
if (self::DOWNLOAD_RESUMABLE_TYPE == $downloadType) {
$contentType = $mimeType;
$postBody = is_string($meta) ? $meta : json_encode($meta);
} else if (self::DOWNLOAD_MEDIA_TYPE == $downloadType) {
$contentType = $mimeType;
$postBody = $this->data;
} else if (self::DOWNLOAD_MULTIPART_TYPE == $downloadType) {
// This is a multipart/related download.
$boundary = $this->boundary ? $this->boundary : mt_rand();
$boundary = str_replace('"', '', $boundary);
$contentType = 'multipart/related; boundary=' . $boundary;
$related = "--$boundary\r\n";
$related .= "Content-Type: application/json; charset=UTF-8\r\n";
$related .= "\r\n" . json_encode($meta) . "\r\n";
$related .= "--$boundary\r\n";
$related .= "Content-Type: $mimeType\r\n";
$related .= "Content-Transfer-Encoding: base64\r\n";
$related .= "\r\n" . base64_encode($this->data) . "\r\n";
$related .= "--$boundary--";
$postBody = $related;
}
$request = $request->withBody(Psr7\stream_for($postBody));
if (isset($contentType) && $contentType) {
$request = $request->withHeader('content-type', $contentType);
}
return $this->request = $request;
}
/**
* Valid download types:
* - resumable (DOWNLOAD_RESUMABLE_TYPE)
* - media (DOWNLOAD_MEDIA_TYPE)
* - multipart (DOWNLOAD_MULTIPART_TYPE)
* @param $meta
* @return string
* @visible for testing
*/
public function getDownloadType($meta)
{
if ($this->resumable) {
return self::DOWNLOAD_RESUMABLE_TYPE;
}
if (false == $meta && $this->data) {
return self::DOWNLOAD_MEDIA_TYPE;
}
return self::DOWNLOAD_MULTIPART_TYPE;
}
public function getResumeUri()
{
if (is_null($this->resumeUri)) {
//$this->resumeUri = $this->fetchResumeUri();
$this->resumeUri = $this->request->getUri();
}
return $this->resumeUri;
}
private function fetchResumeUri()
{
$result = null;
$body = $this->request->getBody();
if ($body) {
$headers = array(
'content-type' => 'application/json; charset=UTF-8',
'content-length' => $body->getSize(),
'x-download-content-type' => $this->mimeType,
'x-download-content-length' => $this->size,
'expect' => '',
);
foreach ($headers as $key => $value) {
$this->request = $this->request->withHeader($key, $value);
}
}
$response = $this->client->execute($this->request, false);
$location = $response->getHeaderLine('location');
$code = $response->getStatusCode();
if (200 == $code && true == $location) {
return $location;
}
$message = $code;
$body = json_decode((string) $this->request->getBody(), true);
if (isset($body['error']['errors'])) {
$message .= ': ';
foreach ($body['error']['errors'] as $error) {
$message .= "{$error[domain]}, {$error[message]};";
}
$message = rtrim($message, ';');
}
$error = "Failed to start the resumable download (HTTP {$message})";
$this->client->getLogger()->error($error);
throw new Google_Exception($error);
}
private function transformToDownloadUrl()
{
$parts = parse_url((string) $this->request->getUri());
if (!isset($parts['path'])) {
$parts['path'] = '';
}
$parts['path'] = '/download' . $parts['path'];
$uri = Uri::fromParts($parts);
$this->request = $this->request->withUri($uri);
}
public function setChunkSize($chunkSize)
{
$this->chunkSize = $chunkSize;
}
public function getRequest()
{
return $this->request;
}
}
Um es zu nutzen
header('Content-Type: application/octet-stream');
//header('Content-Type: ' . $googledrivefile->getMimeType());
//header('Content-Disposition: attachment; filename='.basename($this->real_file));
header('Content-Disposition: attachment; filename='.$googledrivefile->getTitle());
header('Expires: 0');
header('Pragma: public');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
//header('Content-Length: ' . get_real_size($this->real_file));
header("Content-Description: File Transfer");
header('Content-Length: ' . $googledrivefile->getFileSize());
//readfile($this->real_file);
//readfile(UPLOADED_FILES_FOLDER . $googledrivefile->getTitle());
$chunkSizeBytes = ((int)GD_CHUNKSIZE_DOWNLOAD_MB) * 1024 * 1024;
if ($googledrivefile->getFileSize() <= $chunkSizeBytes)
{
header('Cache-Control: private',false);
header('Content-Transfer-Encoding: binary');
header('Connection: close');
//Call without MediaFileDownload
echo $this->getgoogledriverequestbodyFile($this->googledriveservice, $googledrivefile,$this->logger);
//FV Begin Commented because the file downloaded is corrupted
}
else
{
header('Cache-Control: private',false);
//header("Cache-Control: public");
header('Content-Transfer-Encoding: chunked');
header('Connection: keep-alive');
try {
//Begin Use of MediaFileDownload
$this->logger->info("Begin Use of MediaFileDownload...");
// Call the API with the media upload, defer so it doesn't immediately return.
$this->googledriveclient->setDefer(true);
$request = $this->googledriveservice->files->get($googledrivefile->getId(), array(
'alt' => 'media'));
// $downloadUrl = $googledrivefile->getDownloadUrl(); //for google doc is empty
// //$downloadUrl = "https://www.googleapis.com/drive/v3/files/" . $googledrivefile->getId() . "?alt=media";
// $request = new Request(
// 'GET',
// $downloadUrl
// );
$this->logger->info("mediadownloadfromgoogledriveincurrentfolder request->getUri() [" . $request->getUri() . "]");
// Create a media file upload to represent our upload process.
$media = new Google_Http_MediaFileDownload(
$this->googledriveclient,
$request,
$googledrivefile->getMimeType(),
null,
$this->logger,
true,
$chunkSizeBytes
);
$media->setFileSize($googledrivefile->getFileSize());
$status = true;
$progress = 0;
$previousprogress = 0;
while ($status) {
$this->logger->info("mediadownloadfromgoogledriveincurrentfolder read next chunk ");
$status = $media->nextChunk();
if(!$status)
{
$this->logger->info("mediadownloadfromgoogledriveincurrentfolder an error occured ");
break;
}
$response = $status;
$range = explode('-', $response->getHeaderLine('content-range'));
$this->logger->info("mediadownloadfromgoogledriveincurrentfolder range[1] [" . $range[1] . "]");
$range = explode('/', $range[1]);
$this->logger->info("mediadownloadfromgoogledriveincurrentfolder range[0] [" . $range[0] . "]");
$progress = $range[0];
$mediaSize = $range[1];
$this->logger->info("mediadownloadfromgoogledriveincurrentfolder progress [" . $progress . "]");
$this->logger->info("mediadownloadfromgoogledriveincurrentfolder mediaSize [" . $mediaSize . "]");
if($progress > $previousprogress)
{
//Flush the content
//$contentbody = $response->getBody()->__toString();
$contentbody = $response->getBody();
//$this->logger->info("content " . $contentbody);
//Clean buffer and end buffering
while (ob_get_level()) ob_end_clean();
//Start buffering
//ob_implicit_flush();
if (!ob_get_level()) ob_start();
echo $contentbody;
ob_flush();
flush();
$previousprogress = $progress;
//sleep(1);
//usleep(1000000);
usleep(5000);
}
if(($mediaSize - 1) <= $progress)
{
ob_end_flush();
//Clean buffer and end buffering
while (ob_get_level()) ob_end_clean();
$this->logger->info("mediadownloadfromgoogledriveincurrentfolder (mediaSize - 1) <= progress END OF FILE");
break;
}
}
} catch (Google_Service_Exception $e) {
$this->logger->error("mediadownloadfromgoogledriveincurrentfolder error Google_Service_Exception" . $e->getMessage(),$e);
$this->logger->error("mediadownloadfromgoogledriveincurrentfolder error Google_Service_Exception errors " . var_export($e->getErrors(), true));
} catch (Exception $e) {
$this->logger->error("mediadownloadfromgoogledriveincurrentfolder error " . $e->getMessage(),$e);
}
finally {
$this->googledriveclient->setDefer(false);
$this->logger->info("End Use of MediaFileDownload...");
//End Use of MediaFileDownload
}
}
Ja, ich identifizierte das Speicherproblem in meiner Frage. Diese Frage ist wirklich alt und ich weiß nicht einmal mehr, wofür ich das eigentlich gebraucht habe. Danke für den Beispielcode! – DankMemes