花了几个小时才找到一个 copy() 错误:权限被拒绝,(并且 duly 担心 winXP 上的 chmod),值得指出的是“目标”需要包含实际的文件名!--- 而不仅仅是您希望复制到的文件夹的路径.......
DOH!
希望这能为某人节省数小时的无用调试时间
(PHP 4, PHP 5, PHP 7, PHP 8)
copy — 复制文件
from
源文件路径。
to
目标路径。如果 to
是一个 URL,则如果包装器不支持覆盖现有文件,则复制操作可能会失败。
如果目标文件已存在,它将被覆盖。
context
使用 stream_context_create() 创建的有效上下文资源。
示例 #1 copy() 示例
<?php
$file = 'example.txt';
$newfile = 'example.txt.bak';
if (!copy($file, $newfile)) {
echo "failed to copy $file...\n";
}
?>
花了几个小时才找到一个 copy() 错误:权限被拒绝,(并且 duly 担心 winXP 上的 chmod),值得指出的是“目标”需要包含实际的文件名!--- 而不仅仅是您希望复制到的文件夹的路径.......
DOH!
希望这能为某人节省数小时的无用调试时间
我花了很长时间才发现 copy() 出错的原因是什么。它不会创建任何目录。它只复制到现有路径。因此,请先创建目录。希望我能帮到您,
在 Windows 上,php-7.4.19-Win32-vc15-x64 - copy() 损坏了一个 6GB 的 zip 文件。我们唯一的选择是编写
function file_win_copy( $src, $dst ) {
shell_exec( 'COPY "'.$src.'" "'.$dst.'"');
return file_exists($dest);
}
不要忘记;您可以在远程文件上使用 copy,而不是做杂乱的 fopen 操作。例如
<?php
if(!@copy('http://someserver.com/somefile.zip','./somefile.zip'))
{
$errors= error_get_last();
echo "COPY ERROR: ".$errors['type'];
echo "<br />\n".$errors['message'];
} else {
echo "File copied from remote!";
}
?>
这里是一个我用来删除和复制非空目录的简单脚本。当您不确定文件类型时非常有用。
我将这些用于管理我网站插件的文件夹和 zip 存档。
<?php
// 删除文件和非空目录
function rrmdir($dir) {
if (is_dir($dir)) {
$files = scandir($dir);
foreach ($files as $file)
if ($file != "." && $file != "..") rrmdir("$dir/$file");
rmdir($dir);
}
else if (file_exists($dir)) unlink($dir);
}
// 复制文件和非空目录
function rcopy($src, $dst) {
if (file_exists($dst)) rrmdir($dst);
if (is_dir($src)) {
mkdir($dst);
$files = scandir($src);
foreach ($files as $file)
if ($file != "." && $file != "..") rcopy("$src/$file", "$dst/$file");
}
else if (file_exists($src)) copy($src, $dst);
}
?>
干杯!
如果您需要先确保文件夹存在,这是一个简单的技巧
<?php
$srcfile='C:\File\Whatever\Path\Joe.txt';
$dstfile='G:\Shared\Reports\Joe.txt';
mkdir(dirname($dstfile), 0777, true);
copy($srcfile, $dstfile);
?>
就这么简单。
以下代码片段用于将文件从 Web 服务器下载到本地文件。
它演示了请求的有用自定义(例如设置 User-Agent 和 Referrer,通常由网站要求),以及如何仅在网站上的副本比本地副本更新时才下载文件。
它还演示了响应头部的处理(如果由服务器设置)以确定时间戳和文件名。检查文件类型是因为某些服务器返回带有文本“未找到”页面的 200 OK 返回代码,而不是适当的 404 返回代码。
<?php
// $fURI: 位于 Web 服务器上的文件的 URL
// $target_file: 本地文件的路径
if ( file_exists( $target_file ) ) {
$ifmodhdr = 'If-Modified-Since: '.date( "r", filemtime( $target_file ) )."\r\n";
}
else {
$ifmodhdr = '';
}
// 为 GET 设置请求头,带有用于已修改文件的推荐者,该推荐者会遵循重定向
$arrRequestHeaders = array(
'http'=>array(
'method' =>'GET',
'protocol_version' =>1.1,
'follow_location' =>1,
'header'=> "User-Agent: Anamera-Feed/1.0\r\n" .
"Referer: $source\r\n" .
$ifmodhdr
)
);
$rc = copy( $fURI, $target_file, stream_context_create($arrRequestHeaders) );
// HTTP 请求已完成,保留系统错误(如果有)
if( $rc ) {
if ( fclose( $rc ) ) {
unset( $err );
}
else {
$err = error_get_last();
}
}
else {
$err = error_get_last();
}
// 解析 HTTP 响应头以获取 HTTP 状态,以及文件名、类型、日期信息
// 需要从后开始,以获取可能的重定向头集之后的最后一组头
if ( $http_response_header ) {
for ( $i = sizeof($http_response_header) - 1; $i >= 0; $i-- ) {
if ( preg_match('@^http/\S+ (\S{3,}) (.+)$@i', $http_response_header[$i], $http_status) > 0 ) {
// HTTP 状态头表示我们已到达最后一次请求的响应头的开头
break;
}
elseif ( preg_match('@^(\S+):\s*(.+)\s*$@', $http_response_header[$i], $arrHeader) > 0 ) {
switch ( $arrHeader[1] ) {
case 'Last-Modified':
if ( !isset($http_content_modtime) ) {
$http_content_modtime = strtotime( $arrHeader[2] );
}
break;
case 'Content-Type':
if ( !isset($http_content_image_type) ) {
if ( preg_match('@^image/(\w+)@ims', $arrHeader[2], $arrTokens) > 0 ) {
if ( in_array(strtolower($arrTokens[1]), $arrValidTypes)) {
$http_content_image_type = $arrTokens[1];
break;
}
}
throw new Exception( "错误访问文件 $fURI; 无效的内容类型: $arrHeader[2]", 2);
}
break;
case 'Content-Disposition':
if ( !isset($http_content_filename) && preg_match('@filename\\s*=\\s*(?|"([^"]+)"|([\\S]+));?@ims', $arrHeader[2], $arrTokens) > 0 ) {
$http_content_filename = basename($arrTokens[1]);
}
break;
}
}
}
}
if ( $http_status ) {
// 确保我们拥有良好的 HTTP 状态
switch ( $http_status[1] ) {
case '200':
// 成功:HTTP 状态为“200 OK”
break;
case '304':
throw new Exception( "远程文件不是最新版本: $fURI", $http_status[1] );
break;
case '404':
throw new Exception( "远程文件未找到: $fURI", $http_status[1] );
break;
default:
throw new Exception( "HTTP 错误,$http_status[2],访问 $fURI", $http_status[1] );
break;
}
}
elseif ( $err ) {
// 协议/通信错误
throw new Exception( $err['message']/*."; Remote file: $fURI"*/, $err['type'] );
}
else {
// 没有 HTTP 状态,也没有错误
throw new customException( "未知 HTTP 响应访问 $fURI: $http_response_header[0]", -1 );
}
?>
注意
1. 目前 copy() 无法正确处理 304 响应代码。它不会执行复制(可能设置 RC),而是会用一个零长度文件覆盖目标文件。
2. 当使用 HTTP 1.1 协议时,可能存在访问远程文件列表的问题。如果遇到超时错误,请尝试使用默认的 1.0 协议版本。
这是一个简单的递归函数,用于复制整个目录
注意,需要自行检查以确保首次调用时目录存在。
<?php
function recurse_copy($src,$dst) {
$dir = opendir($src);
@mkdir($dst);
while(false !== ( $file = readdir($dir)) ) {
if (( $file != '.' ) && ( $file != '..' )) {
if ( is_dir($src . '/' . $file) ) {
recurse_copy($src . '/' . $file,$dst . '/' . $file);
}
else {
copy($src . '/' . $file,$dst . '/' . $file);
}
}
}
closedir($dir);
}
?>
一些主机禁用 copy() 函数,并称这是出于安全考虑
而对于某些人来说,复制功能至关重要,所以这里有一个简单的函数,实现与 copy 函数相同的效果
如何利用 PHP 的智能帮助我们,例如像 PHP 一样
<?php
function copyemz($file1,$file2){
$contentx =@file_get_contents($file1);
$openedfile = fopen($file2, "w");
fwrite($openedfile, $contentx);
fclose($openedfile);
if ($contentx === FALSE) {
$status=false;
}else $status=true;
return $status;
}
?>
在 Windows 8.1 下,将大型文件从一个 NTFS 文件系统复制到另一个 NTFS 文件系统,结果只会复制前 4 GiB,其余文件将被忽略。
所以,如果你认为有大于 4 GiB 的文件,而不是执行
copy($source,$destination);
最好执行类似
exec("xcopy $source $destination");
我将检查这个问题在 Linux 下是否也存在。
这取决于 PHP 是否以 64 位模式编译?
在 Windows 上(不确定 Linux 是否一样),copy 会覆盖现有文件,但不会更改现有文件名的案例。
换句话说,如果我有一个名为 "Myfile.txt" 的文件,我使用名为 "MyFile.txt" 的文件通过 copy 覆盖它,它会覆盖它,但文件名将保持 "Myfile.txt"。
如果这是一个问题(对我来说就是),请先使用 unlink 删除现有文件。
如果你在 Windows 机器上运行 Apache/PHP,包含日语字符的路径和文件名将无法正确处理。
使用以下代码,你可以将网络路径转换为适当的编码,以便 Windows 能够理解在哪里查找
<?PHP
function convertPath($path)
{
//将网络路径拆分为各部分
$parts = explode('\\',$path);
// 将每个部分转换为 SJIS
foreach($parts as $index => $part)
{
$parts[$index] = iconv('UTF-8','SJIS//IGNORE',$part);
}
// 将网络路径重新组合
return implode('\\',$parts);
}
$oldname = convertPath('c:/Temp/ほげほげ.pdf');
$newname = convertPath('\\\\PFSV0100\\DATA\\06:個人別\\333328_ほげほげ\\test_あいうえお.pdf');
copy($oldname, $newname);
?>
但是,有一些字符无法正确转换,例如 '②' 和 '﨑',因为没有相应的 SJIS 等效字符。
就递归复制而言,类似这样的代码对我有用
<?php
$output = shell_exec( " cp -r -a dir_source/* dir_dest 2>&1 " )
echo $output
?>
当然,你需要确保所有权限都已清除。你可以执行必要的操作来使用变量。
你也可以使用以下代码来创建目标目录
<?php
shell_exec( " cp -r -a dir_source dir_dest 2>&1 " )
?>
如果目录 "dir_dest" 不存在,这将创建一个新的目录。但这在你的情况不明确的情况下有点冒险,你想要继续进行备份等等,因为如果你执行两次,最终会得到
dir_destination/dir_source
为了避免这种情况,可以执行类似的代码
<?php
shell_exec( " mkdir dir_dest; cp -r -a dir_source/* dir_dest 2>&1 " )
?>
也许有人可以告诉我什么时候或为什么使用这里看到的这些 PHP 代码会更好。
感谢大家的评论,我编写了这个函数来完全支持文件和目录的复制。
你可能已经注意到,有些功能还没有时间实现,但如果你有时间来实现它们,甚至添加更多酷炫的功能,请告诉我。你可以在此处找到我 http://sina.salek.ws/en/contact
PS: 对我来说非常有用,希望你也觉得有用。
<?php
/**
* 将文件或文件夹从源复制到目标,可以递归复制,非常智能。
* 如果目标文件或目录路径不存在,它将递归地创建它。
* 情况:
* - Src:/home/test/file.txt ,Dst:/home/test/b ,Result:/home/test/b -> 如果源是文件,将文件.txt 复制到目标,名称为 b
* - Src:/home/test/file.txt ,Dst:/home/test/b/ ,Result:/home/test/b/file.txt -> 如果源是文件,则创建 b 目录(如果不存在),并将文件.txt 复制到其中
* - Src:/home/test ,Dst:/home/ ,Result:/home/test/** -> 如果源是目录,则将 test 目录及其所有内容复制到目标
* - Src:/home/test/ ,Dst:/home/ ,Result:/home/**-> 如果源是目录,则将其内容复制到目标
* - Src:/home/test ,Dst:/home/test2 ,Result:/home/test2/** -> 如果源是目录,则将其及其内容复制到目标,名称为 test2
* - Src:/home/test/ ,Dst:/home/test2 ,Result:->/home/test2/** 如果源是目录,则将其及其内容复制到目标,名称为 test2
* @todo
* - 应该有回滚技术,以便在复制不成功时可以撤消复制
* - 应该可以关闭自动目标技术
* - 支持回调函数
* - 可能会防止共享环境中的一些问题:http://us3.php.net/umask
* @param $source //文件或文件夹
* @param $dest ///文件或文件夹
* @param $options //文件夹权限,文件权限
* @return boolean
*/
function smartCopy($source, $dest, $options=array('folderPermission'=>0755,'filePermission'=>0755))
{
$result=false;
if (is_file($source)) {
if ($dest[strlen($dest)-1]=='/') {
if (!file_exists($dest)) {
cmfcDirectory::makeAll($dest,$options['folderPermission'],true);
}
$__dest=$dest."/".basename($source);
} else {
$__dest=$dest;
}
$result=copy($source, $__dest);
chmod($__dest,$options['filePermission']);
} elseif(is_dir($source)) {
if ($dest[strlen($dest)-1]=='/') {
if ($source[strlen($source)-1]=='/') {
//仅复制内容
} else {
//更改父目录本身及其内容
$dest=$dest.basename($source);
@mkdir($dest);
chmod($dest,$options['filePermission']);
}
} else {
if ($source[strlen($source)-1]=='/') {
//使用新名称复制父目录及其所有内容
@mkdir($dest,$options['folderPermission']);
chmod($dest,$options['filePermission']);
} else {
//使用新名称复制父目录及其所有内容
@mkdir($dest,$options['folderPermission']);
chmod($dest,$options['filePermission']);
}
}
$dirHandle=opendir($source);
while($file=readdir($dirHandle))
{
if($file!="." && $file!="..")
{
if(!is_dir($source."/".$file)) {
$__dest=$dest."/".$file;
} else {
$__dest=$dest."/".$file;
}
//echo "$source/$file ||| $__dest<br />";
$result=smartCopy($source."/".$file, $__dest, $options);
}
}
closedir($dirHandle);
} else {
$result=false;
}
return $result;
}
?>
我找不到原因,但在某些环境中,复制调用的次数可能会受到限制,并引发警告。
警告:copy(myurl):无法打开流:操作失败
我不得不找到另一种方式来检索我的数据。
如果您在配置不当的服务器上运行的应用程序遇到 ssl 错误,并且无法修复服务器配置,这将是一个临时的解决方法。
//禁用 ssl 验证以避免 ssl 错误
$contextOptions = array(
"ssl" => array(
"verify_peer" => false,
"verify_peer_name" => false,
),);
copy($filename, $attachFileName,stream_context_create($contextOptions));
您也可以尝试使用 Shell 命令 xcopy 将文件/文件夹从一个位置移动/复制到另一个位置。
以下是代码
<?php
exec('xcopy c:\\myfolder d:\\myfolder /e/i', $a, $a1);
?>
执行此命令将移动文件夹及其所有内容到目标。
-adnan
似乎您只能对通过表单上传的临时文件使用 move_uploaded_file() 一次。最有可能的是,此函数采取的操作在将临时文件移动到文件系统中的永久位置后会将其销毁。
尝试在同一个 PHP 脚本中再次使用该函数将返回 false,并且不会移动文件。
当一个上传的图像不需要调整大小(小于大小阈值)时,我遇到了这种情况,在将临时上传移动到“originals”目录后,尝试再次将临时文件移动到另一个文件夹。
这种行为是可以理解的,但要注意 - 在这种情况下,我只是对已经上传的文件使用了 copy()。
当我最近需要将几百万个小文件(< 1kb)从一个 NAS 复制到另一个 NAS,并且由于某些原因必须逐个文件进行复制时,我将以下函数与 copy($src, $dest) 和 shell_exec("cp -r $src $dest") 进行了比较。
令人惊讶的是,stream_copy 似乎略快(至少在这种特定的情况下)。
<?php
function stream_copy($src, $dest)
{
$fsrc = fopen($src,'r');
$fdest = fopen($dest,'w+');
$len = stream_copy_to_stream($fsrc,$fdest);
fclose($fsrc);
fclose($fdest);
return $len;
}
?>
如果您遇到“无法打开流:权限被拒绝”问题。
检查 PHP 运行的用户是否对文件路径的每个目录具有“X”权限。
它需要它来访问文件。
如果您的文件是:/path/to/test-in.txt
您应该对
/path
/path/to
具有 X 权限,以及对 /path/to/test-in.txt 具有读取权限。
我自己的用 PHP 编写的“cp -R”。
希望它适合您的情况。我将它用在我的 CMS 的基于 Web 的文件管理器中。
<?php
define('DS', DIRECTORY_SEPARATOR); // 我在代码中总是使用这种简写形式。
function copy_r( $path, $dest )
{
if( is_dir($path) )
{
@mkdir( $dest );
$objects = scandir($path);
if( sizeof($objects) > 0 )
{
foreach( $objects as $file )
{
if( $file == "." || $file == ".." )
continue;
// 继续
if( is_dir( $path.DS.$file ) )
{
copy_r( $path.DS.$file, $dest.DS.$file );
}
else
{
copy( $path.DS.$file, $dest.DS.$file );
}
}
}
return true;
}
elseif( is_file($path) )
{
return copy($path, $dest);
}
else
{
return false;
}
}
?>
此函数创建用于复制给定文件的新文件名,其行为主要从 OS X Finder (*1) 中借鉴而来。
注意,它*不会*实际复制文件,它只是返回新名称。我需要它能够独立于数据源(文件系统、ftp 等)工作。
它还尝试尽可能整洁地匹配当前名称
foo.txt -> foo copy.txt -> foo copy 1.txt -> foo copy 2.txt [等等]
foo.bar.baz.jpg -> foo.bar.baz copy.jpg
foobar -> foobar copy -> foobar copy 1 [等等]
".txt" -> .txt copy, 以及 "txt." -> txt. copy
file.longextension -> file.longextension copy
它会不断尝试,直到找到一个在 $list 中尚未使用的名称,或者直到它循环了 500 次(可以根据需要更改)。
如果重命名的文件长度超过最大文件名长度,它会从添加“copy”之前的位置开始砍掉结尾部分:reallylong...filename.txt -> reallylong...filena copy.txt
<?php
// $orig = 当然,当前名称
// $list = 目标目录中的文件名数组(如果没有给出,它仍然会返回一个新名称)
// $max = 文件名的最大长度
function duplicate_name($orig, $list = array(), $max = 64) {
$ext = '';
$counter = 0;
$list = (array) $list;
$max = (int) $max;
$newname = $orig;
do {
$name = $newname; # 输入名称,输出新名称
if (preg_match('/ copy$| copy \d+$/', $name, $matches)) {
// 甚至不要检查扩展名,名称以 " copy[ 数字]" 结尾
// 以下面的正则表达式匹配任何中间至少有一个句点且扩展名长度为 1-5 个字符的字符
}elseif (preg_match('/(.+)\.([^.]{1,5})$/', $name, $parts)) {
// 拆分为名称和扩展名
list($name, $ext) = array($parts[1], $parts[2]);
}
if (preg_match('/ copy (\d+)$/', $name, $digits)) {
$newname = substr($name, 0, - strlen($digits[1])) . ($digits[1] + 1);
# $cutlen 仅用于最后检查最大文件名长度的位
$cutlen = 6 + strlen($digits[1]+1); // ' copy ' + 数字
}elseif(preg_match('/ copy$/', $name, $digits)) {
$newname = $name . ' 1';
$cutlen = 7; // ' copy' + ' 1'
}else{
$newname = $name . ' copy';
$cutlen = 5; // ' copy'
}
if ($ext) {
$newname .= '.' . $ext;
$cutlen += strlen($ext) + 1;
}
if ($max > 0) {
if (strlen($newname) > $max) {
$newname = substr($newname, 0, max($max - $cutlen, 0)) . substr($newname, -$cutlen);
if (strlen($newname) > $max) {echo "duplicate_name() error: 无法将新名称保持在给定最大长度之下。\n"; return false;}
}
}
if ($counter++ > 500) {echo "duplicate_name() error: 文件名称过于相似或无限循环。\n"; return false;}
} while (in_array($newname, $list));
return $newname;
}
?>
*1) Finder 似乎会检查扩展名与已知扩展名的列表,此函数认为,如果扩展名长度为 5 个字符或更少,则该扩展名有效。
ps. 对占用如此大的空间表示歉意!:-)
直到我在文件路径中添加了 dirname(__FILE__),复制才对我有效。
copy(dirname(__FILE__).$tempdata,dirname(__FILE__).$filepath)