ویژگی تصویر

تابع stream_copy_to_stream() در PHP

  /  PHP   /  تابع stream_copy_to_stream() در PHP
بنر تبلیغاتی الف
آموزش PHP

تابع stream_copy_to_stream() یکی از توابع مفید PHP برای کپی داده‌ها بین دو استریم (resource) است. این تابع برای عملیات‌هایی که نیاز به انتقال داده‌ها از یک منبع به مقصد دارند — مانند کپی فایل‌ها، هدایت ورودی خام (php://input) به فایل، یا انتقال بین سوکت‌ها — بسیار مناسب و کارآمد است.

امضای تابع و توضیح پارامترها

امضاint stream_copy_to_stream(resource $source, resource $dest [, int $maxlength = -1 [, int $offset = 0 ]])
پارامترها
  • $source: منبع استریم (مثلاً fopen(…)).
  • $dest: مقصد استریم که داده‌ها در آن نوشته می‌شوند.
  • $maxlength: حداکثر بایت‌هایی که باید کپی شوند. مقدار پیش‌فرض -1 یعنی تا پایان منبع.
  • $offset: در صورت امکان، ابتدا روی منبع به این آفست seek می‌کند. فقط روی استریم‌های قابل seek کار خواهد کرد.
مقدار بازگشتیتعداد بایت‌های کپی‌شده (int) یا false در صورت خطا.

نکات مهم

  • پارامتر $offset زمانی کاربردی است که $source قابل seek باشد (مانند فایل‌ها). روی سوکت‌ها یا php://input قابل اطمینان نیست.
  • در عمل، این تابع معمولاً از حافظهٔ کمتری نسبت به خواندن کامل محتوای استریم در متغیر و سپس fwrite برخوردار است؛ بنابراین برای فایل‌های بزرگ مناسب است.
  • در صورت استفاده از $maxlength می‌توانید کپی جزئی انجام دهید (مثلاً کپی نخستین 1 مگابایت).

مثال‌های کاربردی

مثال ۱ — کپی فایل به فایل (ساده و امن)

// Copy from one file to another
$src = fopen('large_input.bin', 'rb');
$dst = fopen('backup.bin', 'wb');

if ($src === false || $dst === false) {
    throw new RuntimeException('Cannot open files');
}

$bytes = stream_copy_to_stream($src, $dst);

fclose($src);
fclose($dst);

echo "Copied $bytes bytesn";

این کد فایل large_input.bin را باز می‌کند و محتوا را مستقیماً به backup.bin می‌نویسد. در مقایسه با خواندن کامل فایل با file_get_contents و سپس file_put_contents، این روش حافظهٔ کمتری مصرف می‌کند و سریع است.

مثال ۲ — خواندن از ورودی خام و ذخیره به فایل

// Save raw POST body to a temporary file
$in = fopen('php://input', 'rb');
$tmp = fopen('php://temp', 'w+');

if ($in && $tmp) {
    $bytes = stream_copy_to_stream($in, $tmp);
    rewind($tmp);
    // Now $tmp contains the request body; you can process it.
}

در این مثال دادهٔ ورودی HTTP (بدون پارس شدن مثل آپلودهای خام JSON یا وب‌هوک‌ها) به یک استریم موقت نوشته می‌شود. استریم موقت (php://temp) تا سقف حافظه در رم می‌ماند و بعد از آن به فایل تمپ منتقل می‌شود؛ بنابراین برای پردازش دادهٔ متوسط مناسب است.

مثال ۳ — کپی بخشی از فایل با استفاده از maxlength و offset

// Copy 1MB from offset 2MB of the source file
$src = fopen('bigfile.dat', 'rb');
$dst = fopen('part.dat', 'wb');

$bytes = stream_copy_to_stream($src, $dst, 1024 * 1024, 2 * 1024 * 1024);

fclose($src);
fclose($dst);

این نمونه نشان می‌دهد که می‌توان از $maxlength و $offset برای کپی بخش خاصی از فایل استفاده کرد. توجه داشته باشید که اگر $src قابل seek نباشد، تعیین $offset ممکن است موفق نباشد یا به false منجر شود.

خطاها و مدیریت استثناها

قبل از فراخوانی تابع بهتر است بررسی کنید که منابع (resource) معتبر هستند. تابع در صورت بروز خطا false برمی‌گرداند؛ بنابراین همیشه مقدار بازگشتی را بررسی کنید و در صورت نیاز پیام خطا یا لاگ مناسب ثبت کنید.

نمونه کنترل خطا

$src = @fopen('file.bin', 'rb');
$dst = @fopen('out.bin', 'wb');

if (!$src || !$dst) {
    error_log('Failed to open streams');
} else {
    $copied = stream_copy_to_stream($src, $dst);
    if ($copied === false) {
        error_log('stream_copy_to_stream failed');
    }
    fclose($src);
    fclose($dst);
}

در این کد از چک ساده برای جلوگیری از ادامهٔ عملیات در صورت باز نشدن فایل‌ها استفاده شده است. در محیط تولیدی ممکن است بخواهید با try/catch و استثناها مدیریت پیشرفته‌تری انجام دهید.

موارد پیشرفته و نکات بهینه‌سازی

  • استفاده از فیلترهای استریم (stream_filter_append) برای پردازش آنلاین: مثلاً فشرده‌سازی یا تبدیل کدینگ هنگام کپی. این ترکیب بسیار قوی است چون تغییر داده بدون بارگزاری تمام محتوا در حافظه انجام می‌شود.
  • مراقبت از استریم‌های غیرقابل seek: برای سوکت‌ها یا برخی ورودی‌ها، offset عملی نیست. اگر نیاز به اسکیپ دارید، باید داده‌ها را خوانده و دور بریزید یا از مکانیزم‌های دیگر استفاده کنید.
  • به خاطر داشته باشید که stream_copy_to_stream ممکن است در برخی نسخه‌ها با رفتارهای متفاوت در خصوص non-blocking کار کند؛ برای کار با سوکت‌ها از گزینه‌های stream_set_blocking استفاده کنید.

مزایا نسبت به fgets/fread + fwrite در حلقه

  • کد ساده‌تر و خواناتر.
  • ممکن است پیاده‌سازی داخلی بهینه‌تری داشته باشد و کمتر حافظه مصرف کند.
  • پشتیبانی از $maxlength و $offset به صورت مستقیم.

جمع‌بندی و استفاده‌های پیشنهادی

تابع stream_copy_to_stream() ابزار ساده و قدرتمندی برای جابجایی داده بین استریم‌ها است. برای کپی فایل‌های بزرگ، انتقال داده از/به استریم‌های PHP (مثل php://input یا php://temp) و زمانی که می‌خواهید پردازش‌هایی را به صورت جریان‌محور انجام دهید، بسیار مناسب است. هنگام کار با استریم‌های غیرقابل seek یا سوکت‌ها مراقب offset باشید و همواره مقدار بازگشتی را برای تشخیص خطا چک کنید.

اگر نیاز داشته باشید می‌توان مثال‌هایی از ترکیب این تابع با stream_filter_append یا کار با سوکت‌ها فراهم کرد.

آیا این مطلب برای شما مفید بود ؟

خیر
بله
موضوعات شما در انجمن: