Subversion Repositories cheapmusic

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
25 - 1
<?php
2
/**
3
 * Copyright 2012 Google Inc.
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *     http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
 
18
/**
19
 * @author Chirag Shah <chirags@google.com>
20
 *
21
 */
22
class Google_MediaFileUpload {
23
  const UPLOAD_MEDIA_TYPE = 'media';
24
  const UPLOAD_MULTIPART_TYPE = 'multipart';
25
  const UPLOAD_RESUMABLE_TYPE = 'resumable';
26
 
27
  /** @var string $mimeType */
28
  public $mimeType;
29
 
30
  /** @var string $data */
31
  public $data;
32
 
33
  /** @var bool $resumable */
34
  public $resumable;
35
 
36
  /** @var int $chunkSize */
37
  public $chunkSize;
38
 
39
  /** @var int $size */
40
  public $size;
41
 
42
  /** @var string $resumeUri */
43
  public $resumeUri;
44
 
45
  /** @var int $progress */
46
  public $progress;
47
 
48
  /**
49
   * @param $mimeType string
50
   * @param $data string The bytes you want to upload.
51
   * @param $resumable bool
52
   * @param bool $chunkSize File will be uploaded in chunks of this many bytes.
53
   * only used if resumable=True
54
   */
55
  public function __construct($mimeType, $data, $resumable=false, $chunkSize=false) {
56
    $this->mimeType = $mimeType;
57
    $this->data = $data;
58
    $this->size = strlen($this->data);
59
    $this->resumable = $resumable;
60
    if(!$chunkSize) {
61
      $this->chunkSize = 256 * 1024;
62
    }
63
 
64
    $this->progress = 0;
65
  }
66
 
67
  /**
68
   * @static
69
   * @param $meta
70
   * @param $params
71
   * @return array|bool
72
   */
73
  public static function process($meta, &$params) {
74
    $payload = array();
75
    $meta = is_string($meta) ? json_decode($meta, true) : $meta;
76
    $uploadType = self::getUploadType($meta, $payload, $params);
77
    if (!$uploadType) {
78
      // Process as a normal API request.
79
      return false;
80
    }
81
 
82
    // Process as a media upload request.
83
    $params['uploadType'] = array(
84
        'type' => 'string',
85
        'location' => 'query',
86
        'value' => $uploadType,
87
    );
88
 
89
    if (isset($params['file'])) {
90
      // This is a standard file upload with curl.
91
      $file = $params['file']['value'];
92
      unset($params['file']);
93
      return self::processFileUpload($file);
94
    }
95
 
96
    $mimeType = isset($params['mimeType'])
97
        ? $params['mimeType']['value']
98
        : false;
99
    unset($params['mimeType']);
100
 
101
    $data = isset($params['data'])
102
        ? $params['data']['value']
103
        : false;
104
    unset($params['data']);
105
 
106
    if (self::UPLOAD_RESUMABLE_TYPE == $uploadType) {
107
      $payload['content-type'] = $mimeType;
108
 
109
    } elseif (self::UPLOAD_MEDIA_TYPE == $uploadType) {
110
      // This is a simple media upload.
111
      $payload['content-type'] = $mimeType;
112
      $payload['postBody'] = $data;
113
    }
114
 
115
    elseif (self::UPLOAD_MULTIPART_TYPE == $uploadType) {
116
      // This is a multipart/related upload.
117
      $boundary = isset($params['boundary']['value']) ? $params['boundary']['value'] : mt_rand();
118
      $boundary = str_replace('"', '', $boundary);
119
      $payload['content-type'] = 'multipart/related; boundary=' . $boundary;
120
      $related = "--$boundary\r\n";
121
      $related .= "Content-Type: application/json; charset=UTF-8\r\n";
122
      $related .= "\r\n" . json_encode($meta) . "\r\n";
123
      $related .= "--$boundary\r\n";
124
      $related .= "Content-Type: $mimeType\r\n";
125
      $related .= "Content-Transfer-Encoding: base64\r\n";
126
      $related .= "\r\n" . base64_encode($data) . "\r\n";
127
      $related .= "--$boundary--";
128
      $payload['postBody'] = $related;
129
    }
130
 
131
    return $payload;
132
  }
133
 
134
  /**
135
   * Process standard file uploads.
136
   * @param $file
137
   * @internal param $fileName
138
   * @return array Inclues the processed file name.
139
   * @visible For testing.
140
   */
141
  public static function processFileUpload($file) {
142
    if (!$file) return array();
143
    if (substr($file, 0, 1) != '@') {
144
      $file = '@' . $file;
145
    }
146
 
147
    // This is a standard file upload with curl.
148
    return array('postBody' => array('file' => $file));
149
  }
150
 
151
  /**
152
   * Valid upload types:
153
   * - resumable (UPLOAD_RESUMABLE_TYPE)
154
   * - media (UPLOAD_MEDIA_TYPE)
155
   * - multipart (UPLOAD_MULTIPART_TYPE)
156
   * - none (false)
157
   * @param $meta
158
   * @param $payload
159
   * @param $params
160
   * @return bool|string
161
   */
162
  public static function getUploadType($meta, &$payload, &$params) {
163
    if (isset($params['mediaUpload'])
164
        && get_class($params['mediaUpload']['value']) == 'Google_MediaFileUpload') {
165
      $upload = $params['mediaUpload']['value'];
166
      unset($params['mediaUpload']);
167
      $payload['content-type'] = $upload->mimeType;
168
      if (isset($upload->resumable) && $upload->resumable) {
169
        return self::UPLOAD_RESUMABLE_TYPE;
170
      }
171
    }
172
 
173
    // Allow the developer to override the upload type.
174
    if (isset($params['uploadType'])) {
175
      return $params['uploadType']['value'];
176
    }
177
 
178
    $data = isset($params['data']['value'])
179
        ? $params['data']['value'] : false;
180
 
181
    if (false == $data && false == isset($params['file'])) {
182
      // No upload data available.
183
      return false;
184
    }
185
 
186
    if (isset($params['file'])) {
187
      return self::UPLOAD_MEDIA_TYPE;
188
    }
189
 
190
    if (false == $meta) {
191
      return self::UPLOAD_MEDIA_TYPE;
192
    }
193
 
194
    return self::UPLOAD_MULTIPART_TYPE;
195
  }
196
 
197
 
198
  public function nextChunk(Google_HttpRequest $req) {
199
    if (false == $this->resumeUri) {
200
      $this->resumeUri = $this->getResumeUri($req);
201
    }
202
 
203
    $data = substr($this->data, $this->progress, $this->chunkSize);
204
    $lastBytePos = $this->progress + strlen($data) - 1;
205
    $headers = array(
206
      'content-range' => "bytes $this->progress-$lastBytePos/$this->size",
207
      'content-type' => $req->getRequestHeader('content-type'),
208
      'content-length' => $this->chunkSize,
209
      'expect' => '',
210
    );
211
 
212
    $httpRequest = new Google_HttpRequest($this->resumeUri, 'PUT', $headers, $data);
213
    $response = Google_Client::$io->authenticatedRequest($httpRequest);
214
    $code = $response->getResponseHttpCode();
215
    if (308 == $code) {
216
      $range = explode('-', $response->getResponseHeader('range'));
217
      $this->progress = $range[1] + 1;
218
      return false;
219
    } else {
220
      return Google_REST::decodeHttpResponse($response);
221
    }
222
  }
223
 
224
  private function getResumeUri(Google_HttpRequest $httpRequest) {
225
    $result = null;
226
    $body = $httpRequest->getPostBody();
227
    if ($body) {
228
      $httpRequest->setRequestHeaders(array(
229
        'content-type' => 'application/json; charset=UTF-8',
230
        'content-length' => Google_Utils::getStrLen($body),
231
        'x-upload-content-type' => $this->mimeType,
232
        'expect' => '',
233
      ));
234
    }
235
 
236
    $response = Google_Client::$io->makeRequest($httpRequest);
237
    $location = $response->getResponseHeader('location');
238
    $code = $response->getResponseHttpCode();
239
    if (200 == $code && true == $location) {
240
      return $location;
241
    }
242
    throw new Google_Exception("Failed to start the resumable upload");
243
  }
244
}