大文件快速复制stream_copy_to_strea
大文件快速复制
今天看到一篇文章,说是大文件复制,使用该函数stream_copy_to_stream。然后看了一下手册,通过探究,其底层,实际上跟copy差不多。
有篇文章说过,socket_开头的函数,都是c语言底层库提供的函数,php只是简单进行封装了,保留它,只要是底层函数,有更强的描述能力;而stream类的函数,都是php内部,对其进行了抽象封装,提供了更方便的接口。
查看
stream_copy_to_stream
示例:
$f1 = fopen("txt.txt",'r');
$f2 = fopen("txt1.txt",'w+');
stream_copy_to_stream( $f1,$f2);
fclose($f1);
fclose($f2);
大概该函数,跟nodejs说的pipe,go语言中的,io.Copy差不多功能吧。
探究
由于都是标准库,所以代码都在./ext/standard目录下。
首先看,basic_functions.c文件,该文件里面,包含了绝大多数的函数原型。
第3151行左右,看看到了如下:
PHP_FE(copy, arginfo_copy)
其上,有注释,说明该函数的实现是在file.c中。
第3181行:
PHP_FE(stream_copy_to_stream,arginfo_stream_copy_to_stream)
实现在ext\standard\streamsfuncs.c中。
copy
PHP_FUNCTION(copy) { char *source, *target; size_t source_len, target_len; zval *zcontext = NULL; php_stream_context *context; ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_PATH(source, source_len) Z_PARAM_PATH(target, target_len) Z_PARAM_OPTIONAL Z_PARAM_RESOURCE_EX(zcontext, 1, 0) ZEND_PARSE_PARAMETERS_END(); if (php_check_open_basedir(source)) { RETURN_FALSE; } context = php_stream_context_from_zval(zcontext, 0); if (php_copy_file_ctx(source, target, 0, context) == SUCCESS) { RETURN_TRUE; } else { RETURN_FALSE; } } PHPAPI int php_copy_file_ctx(const char *src, const char *dest, int src_flg, php_stream_context *ctx) { php_stream *srcstream = NULL, *deststream = NULL; int ret = FAILURE; php_stream_statbuf src_s, dest_s; switch (php_stream_stat_path_ex(src, 0, &src_s, ctx)) { case -1: /* non-statable stream */ goto safe_to_copy; break; case 0: break; default: /* failed to stat file, does not exist? */ return ret; } if (S_ISDIR(src_s.sb.st_mode)) { php_error_docref(NULL, E_WARNING, "The first argument to copy() function cannot be a directory"); return FAILURE; } switch (php_stream_stat_path_ex(dest, PHP_STREAM_URL_STAT_QUIET | PHP_STREAM_URL_STAT_NOCACHE, &dest_s, ctx)) { case -1: /* non-statable stream */ goto safe_to_copy; break; case 0: break; default: /* failed to stat file, does not exist? */ return ret; } if (S_ISDIR(dest_s.sb.st_mode)) { php_error_docref(NULL, E_WARNING, "The second argument to copy() function cannot be a directory"); return FAILURE; } if (!src_s.sb.st_ino || !dest_s.sb.st_ino) { goto no_stat; } if (src_s.sb.st_ino == dest_s.sb.st_ino && src_s.sb.st_dev == dest_s.sb.st_dev) { return ret; } else { goto safe_to_copy; } no_stat: { char *sp, *dp; int res; if ((sp = expand_filepath(src, NULL)) == NULL) { return ret; } if ((dp = expand_filepath(dest, NULL)) == NULL) { efree(sp); goto safe_to_copy; } res = #ifndef PHP_WIN32 !strcmp(sp, dp); #else !strcasecmp(sp, dp); #endif efree(sp); efree(dp); if (res) { return ret; } } safe_to_copy: srcstream = php_stream_open_wrapper_ex(src, "rb", src_flg | REPORT_ERRORS, NULL, ctx); if (!srcstream) { return ret; } deststream = php_stream_open_wrapper_ex(dest, "wb", REPORT_ERRORS, NULL, ctx); if (srcstream && deststream) { ret = php_stream_copy_to_stream_ex(srcstream, deststream, PHP_STREAM_COPY_ALL, NULL); } if (srcstream) { php_stream_close(srcstream); } if (deststream) { php_stream_close(deststream); } return ret; }stream_copy_to_stream
PHP_FUNCTION(stream_copy_to_stream) { php_stream *src, *dest; zval *zsrc, *zdest; zend_long maxlen = PHP_STREAM_COPY_ALL, pos = 0; size_t len; int ret; ZEND_PARSE_PARAMETERS_START(2, 4) Z_PARAM_RESOURCE(zsrc) Z_PARAM_RESOURCE(zdest) Z_PARAM_OPTIONAL Z_PARAM_LONG(maxlen) Z_PARAM_LONG(pos) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); php_stream_from_zval(src, zsrc); php_stream_from_zval(dest, zdest); if (pos > 0 && php_stream_seek(src, pos, SEEK_SET) < 0) { php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", pos); RETURN_FALSE; } ret = php_stream_copy_to_stream_ex(src, dest, maxlen, &len); if (ret != SUCCESS) { RETURN_FALSE; } RETURN_LONG(len); }
上面代码,发现:
底层都是调用下面的函数。
php_stream_copy_to_stream_ex