|
40 | 40 | * |
41 | 41 | * The caller is responsible for: |
42 | 42 | * - obtaining credentials and passing them in (see {@see self::get_token()}), |
43 | | - * - deciding how long to cache the auth token, |
| 43 | + * - deciding how long to cache the auth token, and |
44 | 44 | * - retrying on HTTP 401 after calling `get_token( ..., true )` for a fresh |
45 | | - * token, and |
46 | | - * - supplying a chunk size for {@see self::download_file()}. |
| 45 | + * token. |
47 | 46 | * |
48 | 47 | * @package seafile-updraft-backup-uploader |
49 | 48 | */ |
|
52 | 51 |
|
53 | 52 | final class SBU_Seafile_API { |
54 | 53 |
|
55 | | - /** |
56 | | - * Default chunk size (bytes) for {@see self::download_file()} when the |
57 | | - * caller passes `$chunk_size <= 0`. Matches the plugin's own default of |
58 | | - * 40 MB per chunk. |
59 | | - */ |
60 | | - const DEFAULT_DOWNLOAD_CHUNK = 20971520; // 20 MB — safe through Cloudflare Tunnel (~100 s connection limit); the plugin runs N of these in parallel via download_chunks_parallel(). |
61 | | - |
62 | 54 | /** |
63 | 55 | * Obtain an auth token. Returns the cached transient unless `$force` is |
64 | 56 | * true, in which case the transient is discarded and a fresh token |
@@ -284,207 +276,6 @@ public static function get_download_link( $url, $token, $rid, $path ) { |
284 | 276 | return $link; |
285 | 277 | } |
286 | 278 |
|
287 | | - /** |
288 | | - * Stream a file from Seafile to a local path, chunking with HTTP Range |
289 | | - * requests when the remote is larger than the requested chunk size. |
290 | | - * Link re-fetch on HTTP 403 (expired signed URL) is handled internally. |
291 | | - * |
292 | | - * @param string $url Seafile base URL. |
293 | | - * @param string $token Auth token. |
294 | | - * @param string $rid Repo ID. |
295 | | - * @param string $path Remote file path. |
296 | | - * @param string $dest Local destination path (will be overwritten). |
297 | | - * @param int $chunk_size Bytes per Range request. Pass <= 0 to use |
298 | | - * {@see self::DEFAULT_DOWNLOAD_CHUNK}. |
299 | | - * @return true|\WP_Error |
300 | | - */ |
301 | | - public static function download_file( $url, $token, $rid, $path, $dest, $chunk_size = 0 ) { |
302 | | - $link = self::get_download_link( $url, $token, $rid, $path ); |
303 | | - if ( is_wp_error( $link ) ) { |
304 | | - return $link; |
305 | | - } |
306 | | - |
307 | | - if ( $chunk_size <= 0 ) { |
308 | | - $chunk_size = self::DEFAULT_DOWNLOAD_CHUNK; |
309 | | - } |
310 | | - |
311 | | - // Get file size via HEAD |
312 | | - $head = wp_remote_head( |
313 | | - $link, |
314 | | - array( |
315 | | - 'timeout' => SBU_TIMEOUT_API, |
316 | | - 'headers' => array( 'Authorization' => 'Token ' . $token ), |
317 | | - ) |
318 | | - ); |
319 | | - $file_size = 0; |
320 | | - if ( ! is_wp_error( $head ) ) { |
321 | | - $file_size = (int) wp_remote_retrieve_header( $head, 'content-length' ); |
322 | | - } |
323 | | - |
324 | | - // Small files or unknown size: download in one go |
325 | | - if ( $file_size <= $chunk_size ) { |
326 | | - $dl = wp_remote_get( |
327 | | - $link, |
328 | | - array( |
329 | | - 'timeout' => SBU_TIMEOUT_DOWNLOAD, |
330 | | - 'stream' => true, |
331 | | - 'filename' => $dest, |
332 | | - 'headers' => array( |
333 | | - 'Authorization' => 'Token ' . $token, |
334 | | - 'Connection' => 'close', |
335 | | - ), |
336 | | - ) |
337 | | - ); |
338 | | - if ( is_wp_error( $dl ) ) { |
339 | | - @unlink( $dest ); |
340 | | - return $dl; |
341 | | - } |
342 | | - $code = wp_remote_retrieve_response_code( $dl ); |
343 | | - // Refresh link on 403 and retry once |
344 | | - if ( $code === 403 ) { |
345 | | - @unlink( $dest ); |
346 | | - $link = self::get_download_link( $url, $token, $rid, $path ); |
347 | | - if ( is_wp_error( $link ) ) { |
348 | | - return $link; |
349 | | - } |
350 | | - $dl = wp_remote_get( |
351 | | - $link, |
352 | | - array( |
353 | | - 'timeout' => SBU_TIMEOUT_DOWNLOAD, |
354 | | - 'stream' => true, |
355 | | - 'filename' => $dest, |
356 | | - 'headers' => array( 'Authorization' => 'Token ' . $token ), |
357 | | - ) |
358 | | - ); |
359 | | - if ( is_wp_error( $dl ) ) { |
360 | | - @unlink( $dest ); |
361 | | - return $dl; |
362 | | - } |
363 | | - $code = wp_remote_retrieve_response_code( $dl ); |
364 | | - } |
365 | | - if ( $code !== 200 ) { |
366 | | - @unlink( $dest ); |
367 | | - return new \WP_Error( 'http', 'HTTP ' . $code ); |
368 | | - } |
369 | | - return true; |
370 | | - } |
371 | | - |
372 | | - // Large files: chunked download with Range headers |
373 | | - @unlink( $dest ); |
374 | | - $offset = 0; |
375 | | - $link_age = time(); |
376 | | - while ( $offset < $file_size ) { |
377 | | - // Check for abort between chunks |
378 | | - wp_cache_delete( 'sbu_abort_flag', 'transient' ); |
379 | | - wp_cache_delete( '_transient_sbu_abort_flag', 'options' ); |
380 | | - if ( get_transient( 'sbu_abort_flag' ) ) { |
381 | | - @unlink( $dest ); |
382 | | - return new \WP_Error( 'aborted', 'Download abgebrochen' ); |
383 | | - } |
384 | | - |
385 | | - // Refresh download link if older than 10 minutes |
386 | | - if ( ( time() - $link_age ) > 600 ) { |
387 | | - $link = self::get_download_link( $url, $token, $rid, $path ); |
388 | | - if ( is_wp_error( $link ) ) { |
389 | | - @unlink( $dest ); |
390 | | - return $link; |
391 | | - } |
392 | | - $link_age = time(); |
393 | | - } |
394 | | - |
395 | | - $end = min( $offset + $chunk_size - 1, $file_size - 1 ); |
396 | | - $tmp = $dest . '.part'; |
397 | | - |
398 | | - $dl = wp_remote_get( |
399 | | - $link, |
400 | | - array( |
401 | | - 'timeout' => SBU_TIMEOUT_DOWNLOAD, |
402 | | - 'stream' => true, |
403 | | - 'filename' => $tmp, |
404 | | - 'headers' => array( |
405 | | - 'Authorization' => 'Token ' . $token, |
406 | | - 'Connection' => 'close', |
407 | | - 'Range' => "bytes={$offset}-{$end}", |
408 | | - ), |
409 | | - ) |
410 | | - ); |
411 | | - |
412 | | - if ( is_wp_error( $dl ) ) { |
413 | | - @unlink( $tmp ); |
414 | | - @unlink( $dest ); |
415 | | - return $dl; |
416 | | - } |
417 | | - |
418 | | - $code = wp_remote_retrieve_response_code( $dl ); |
419 | | - |
420 | | - // Refresh link on 403 and retry this chunk |
421 | | - if ( $code === 403 ) { |
422 | | - @unlink( $tmp ); |
423 | | - $link = self::get_download_link( $url, $token, $rid, $path ); |
424 | | - if ( is_wp_error( $link ) ) { |
425 | | - @unlink( $dest ); |
426 | | - return $link; |
427 | | - } |
428 | | - $link_age = time(); |
429 | | - $dl = wp_remote_get( |
430 | | - $link, |
431 | | - array( |
432 | | - 'timeout' => SBU_TIMEOUT_DOWNLOAD, |
433 | | - 'stream' => true, |
434 | | - 'filename' => $tmp, |
435 | | - 'headers' => array( |
436 | | - 'Authorization' => 'Token ' . $token, |
437 | | - 'Range' => "bytes={$offset}-{$end}", |
438 | | - ), |
439 | | - ) |
440 | | - ); |
441 | | - if ( is_wp_error( $dl ) ) { |
442 | | - @unlink( $tmp ); |
443 | | - @unlink( $dest ); |
444 | | - return $dl; |
445 | | - } |
446 | | - $code = wp_remote_retrieve_response_code( $dl ); |
447 | | - } |
448 | | - |
449 | | - if ( $code !== 206 && $code !== 200 ) { |
450 | | - @unlink( $tmp ); |
451 | | - @unlink( $dest ); |
452 | | - return new \WP_Error( 'http', "HTTP {$code} bei Offset {$offset}" ); |
453 | | - } |
454 | | - |
455 | | - // Append chunk to destination |
456 | | - $fh = fopen( $dest, $offset === 0 ? 'wb' : 'ab' ); |
457 | | - if ( ! $fh ) { |
458 | | - @unlink( $tmp ); |
459 | | - return new \WP_Error( 'io', 'Cannot open dest' ); |
460 | | - } |
461 | | - $th = fopen( $tmp, 'rb' ); |
462 | | - if ( $th ) { |
463 | | - while ( ! feof( $th ) ) { |
464 | | - fwrite( $fh, fread( $th, 65536 ) ); |
465 | | - } |
466 | | - fclose( $th ); |
467 | | - } |
468 | | - fclose( $fh ); |
469 | | - @unlink( $tmp ); |
470 | | - |
471 | | - $written = filesize( $dest ); |
472 | | - $offset = $written; |
473 | | - |
474 | | - // If server returned 200 instead of 206, it sent the whole file |
475 | | - if ( $code === 200 ) { |
476 | | - break; |
477 | | - } |
478 | | - } |
479 | | - |
480 | | - // Verify final size |
481 | | - if ( file_exists( $dest ) && filesize( $dest ) >= $file_size ) { |
482 | | - return true; |
483 | | - } |
484 | | - $actual = file_exists( $dest ) ? filesize( $dest ) : 0; |
485 | | - return new \WP_Error( 'incomplete', "Download unvollständig: {$actual}/{$file_size} Bytes" ); |
486 | | - } |
487 | | - |
488 | 279 | /** |
489 | 280 | * Download the whole file in a single streamed GET — no Range header. |
490 | 281 | * |
|
0 commit comments