PHP 使用 FWRITE 和 SSH2 上传多个文件
PHP upload multiple files using FWRITE and SSH2
我正在尝试将多个文件从本地目录上传到 SFTP 站点。
我可以让它为单个文件工作,但我希望能够上传也有变量名称的多个文件。
$localFile_xml = "C:\xml\Race_" . $value;
chdir($localFile_xml);
//This successfully lists the files
foreach (glob("*.xml") as $filename) {
echo "$filename size " . filesize($filename) . "\n";
}
$remote_XMLfiles = "/FTP/XML/Race_" . $value;
$xmlstream = fopen("ssh2.sftp://$sftp" . $remote_XMLfiles, 'w');
foreach (glob("*.xml") as $filename) {
$xmlfile = file_get_contents($localFile_xml);
fwrite($xmlstream, $xmlfile);
fclose($xmlstream);
}
我相信它在那里,但我无法正确理解最后一点。
非常感谢您
假设远程 SSH 连接有效,并且您在问题中使用的方法适用于单个文件,我相信您的操作顺序需要更正。
正如我在评论中提到的,您的代码似乎试图在本地目录上使用 file_get_contents
,这是不允许的。看起来您在 $xmlstream
上也尝试过相同的操作,这必须针对每个文件而不是目录执行。假设 'C:\xml\Race_' . $value;
是一个类似于 C:\xml\Race_1
的目录而不是文件。
资源验证的一些小问题和 Windows 需要解决的具体问题:
Windows 目录分隔符应该写成 \
(即使当
使用单引号),因为 \
是转义序列
它导致 \x \t \n \r \' \" \
被视为特殊字符。
当使用fopen($path, $mode)
时,建议将b
标志指定为模式的最后一个字符,以确保文件是
二进制安全 (verbatim) 并避免操作系统之间的歧义。或者在 Windows 上指定 t
模式以透明地将 \n
转换为 \r\n
(仅适用于纯文本文件).
$mode = 'rb'
(二进制安全读取)
$mode = 'rt'
(文本模式翻译阅读)
使用网络流时,建议测试流是否已成功写入其所有内容。我在 PHP 手册中提供了下面的 fwrite_stream
函数。
例子
try {
//--- example purposes only ---
//use your own ssh2_connnect, ssh2_auth_password, ssh2_sftp
if (!$ssh2 = ssh2_connect('192.168.56.101', 22)) {
throw new \RuntimeException('Unable to connect to remote host');
}
if (!ssh2_auth_password($ssh2, 'root', '')) {
throw new \RuntimeException('Unable to Authenticate');
}
if (!$sftp = ssh2_sftp($ssh2)) {
throw new \RuntimeException('Unable to initialize SFTP');
}
$value = '1';
//--- end example purposes only ---
$localFile_xml = 'C:\xml\Race_' . $value;
if (!$localFile_xml || !is_dir($localFile_xml)) {
throw new \RuntimeException('Unable to retrieve local directory');
}
//retrieve list of XML files
$iterator = new \GlobIterator($localFile_xml . '/*.xml',
\FilesystemIterator::KEY_AS_PATHNAME |
\FilesystemIterator::CURRENT_AS_FILEINFO |
\FilesystemIterator::SKIP_DOTS
);
if (!$iterator->count()) {
throw new \RuntimeException('Unable to retrieve local files');
}
$success = [];
$remote_XMLfiles = '/FTP/XML/Race_' . $value;
$remote_XMLpath = "ssh2.sftp://$sftp" . $remote_XMLfiles;
//ensure the remote directory exists
if (!@mkdir($remote_XMLpath, 0777, true) && !is_dir($remote_XMLpath)) {
throw new \RuntimeException(sprintf('Unable to create remote directory "%s"', $remote_XMLpath));
}
/**
* @var string $filepath
* @var \SplFileInfo $fileinfo
*/
foreach ($iterator as $filepath => $fileinfo) {
$filesize = $fileinfo->getSize();
printf("%s size %d\n", $filepath, $filesize);
try {
//open local file resource for binary-safe reading
$xmlObj = $fileinfo->openFile('rb');
//retrieve entire file contents
if (!$xmlData = $xmlObj->fread($filesize)) {
//do not permit empty files
printf("No data found for \"%s\"\n", $filepath);
continue;
}
} finally {
//shortcut to close the opened local file resource on success or fail
$xmlObj = null;
unset($xmlObj);
}
try {
$remote_filepath = $remote_XMLpath . '/' . $fileinfo->getBasename();
//open a remote file resource for binary-safe writing
//using current filename, overwriting the file if it already exists
if (!$xmlstream = fopen($remote_filepath, 'wb')) {
throw new \RuntimeException(sprintf('Unable to create remote file "%s"', $remote_filepath));
}
//write the local file data to the remote file stream
if (false !== ($bytes = fwrite_stream($xmlstream, $xmlData))) {
$success[] = [
'filepath' => $filepath,
'remote_filepath' => $remote_filepath,
'bytes' => $bytes,
];
}
} finally {
//shortcut to ensure the xmlstream is closed on success or failure
if (isset($xmlstream) && is_resource($xmlstream)) {
fclose($xmlstream);
}
}
}
//testing purposes only to show the resulting uploads
if (!empty($success)) {
var_export($success);
}
} finally {
//shortcut to disconnect the ssh2 session on success or failure
$sftp = null;
unset($sftp);
if (isset($ssh2) && is_resource($ssh2)) {
ssh2_disconnect($ssh2);
}
}
/*
* Taken from PHP Manual
* Writing to a network stream may end before the whole string is written.
* Return value of fwrite() may be checked
*/
function fwrite_stream($fp, $string)
{
for ($written = 0, $writtenMax = strlen($string); $written < $writtenMax; $written += $fwrite) {
$fwrite = fwrite($fp, substr($string, $written));
if (false === $fwrite) {
return $written;
}
}
return $written;
}
NOTE
All file operations will be created using the ssh2_auth_password
user as the owner/group. You must ensure the specified user has read
and write access to the desired directories.
Use the appropriate file masks to ensure desired file/directory permissions
0777
(default) allows everyone to read, write, execute!
0750
is typically desired for directories
0640
is typically desired for individual files
- use
chmod($path, 0750)
to change permissions on the remote file(s)
- use
chown($path, 'user')
to change the owner on the remote file(s)
- use
chgrp($path, 'group')
to change the group on the remote file(s)
结果
C:\xml\Race_1\file1.xml size 9
C:\xml\Race_1\file2.xml size 11
array (
0 =>
array (
'filepath' => 'C:\xml\Race_1\file1.xml',
'remote_filepath' => 'ssh2.sftp://Resource id #5/FTP/XML/Race_1/file1.xml',
'bytes' => 9,
),
1 =>
array (
'filepath' => 'C:\xml\Race_1\file2.xml',
'remote_filepath' => 'ssh2.sftp://Resource id #5/FTP/XML/Race_1/file2.xml',
'bytes' => 11,
),
)
我正在尝试将多个文件从本地目录上传到 SFTP 站点。
我可以让它为单个文件工作,但我希望能够上传也有变量名称的多个文件。
$localFile_xml = "C:\xml\Race_" . $value;
chdir($localFile_xml);
//This successfully lists the files
foreach (glob("*.xml") as $filename) {
echo "$filename size " . filesize($filename) . "\n";
}
$remote_XMLfiles = "/FTP/XML/Race_" . $value;
$xmlstream = fopen("ssh2.sftp://$sftp" . $remote_XMLfiles, 'w');
foreach (glob("*.xml") as $filename) {
$xmlfile = file_get_contents($localFile_xml);
fwrite($xmlstream, $xmlfile);
fclose($xmlstream);
}
我相信它在那里,但我无法正确理解最后一点。
非常感谢您
假设远程 SSH 连接有效,并且您在问题中使用的方法适用于单个文件,我相信您的操作顺序需要更正。
正如我在评论中提到的,您的代码似乎试图在本地目录上使用 file_get_contents
,这是不允许的。看起来您在 $xmlstream
上也尝试过相同的操作,这必须针对每个文件而不是目录执行。假设 'C:\xml\Race_' . $value;
是一个类似于 C:\xml\Race_1
的目录而不是文件。
资源验证的一些小问题和 Windows 需要解决的具体问题:
Windows 目录分隔符应该写成
\
(即使当 使用单引号),因为\
是转义序列 它导致\x \t \n \r \' \" \
被视为特殊字符。当使用
fopen($path, $mode)
时,建议将b
标志指定为模式的最后一个字符,以确保文件是 二进制安全 (verbatim) 并避免操作系统之间的歧义。或者在 Windows 上指定t
模式以透明地将\n
转换为\r\n
(仅适用于纯文本文件).$mode = 'rb'
(二进制安全读取)$mode = 'rt'
(文本模式翻译阅读)
使用网络流时,建议测试流是否已成功写入其所有内容。我在 PHP 手册中提供了下面的
fwrite_stream
函数。
例子
try {
//--- example purposes only ---
//use your own ssh2_connnect, ssh2_auth_password, ssh2_sftp
if (!$ssh2 = ssh2_connect('192.168.56.101', 22)) {
throw new \RuntimeException('Unable to connect to remote host');
}
if (!ssh2_auth_password($ssh2, 'root', '')) {
throw new \RuntimeException('Unable to Authenticate');
}
if (!$sftp = ssh2_sftp($ssh2)) {
throw new \RuntimeException('Unable to initialize SFTP');
}
$value = '1';
//--- end example purposes only ---
$localFile_xml = 'C:\xml\Race_' . $value;
if (!$localFile_xml || !is_dir($localFile_xml)) {
throw new \RuntimeException('Unable to retrieve local directory');
}
//retrieve list of XML files
$iterator = new \GlobIterator($localFile_xml . '/*.xml',
\FilesystemIterator::KEY_AS_PATHNAME |
\FilesystemIterator::CURRENT_AS_FILEINFO |
\FilesystemIterator::SKIP_DOTS
);
if (!$iterator->count()) {
throw new \RuntimeException('Unable to retrieve local files');
}
$success = [];
$remote_XMLfiles = '/FTP/XML/Race_' . $value;
$remote_XMLpath = "ssh2.sftp://$sftp" . $remote_XMLfiles;
//ensure the remote directory exists
if (!@mkdir($remote_XMLpath, 0777, true) && !is_dir($remote_XMLpath)) {
throw new \RuntimeException(sprintf('Unable to create remote directory "%s"', $remote_XMLpath));
}
/**
* @var string $filepath
* @var \SplFileInfo $fileinfo
*/
foreach ($iterator as $filepath => $fileinfo) {
$filesize = $fileinfo->getSize();
printf("%s size %d\n", $filepath, $filesize);
try {
//open local file resource for binary-safe reading
$xmlObj = $fileinfo->openFile('rb');
//retrieve entire file contents
if (!$xmlData = $xmlObj->fread($filesize)) {
//do not permit empty files
printf("No data found for \"%s\"\n", $filepath);
continue;
}
} finally {
//shortcut to close the opened local file resource on success or fail
$xmlObj = null;
unset($xmlObj);
}
try {
$remote_filepath = $remote_XMLpath . '/' . $fileinfo->getBasename();
//open a remote file resource for binary-safe writing
//using current filename, overwriting the file if it already exists
if (!$xmlstream = fopen($remote_filepath, 'wb')) {
throw new \RuntimeException(sprintf('Unable to create remote file "%s"', $remote_filepath));
}
//write the local file data to the remote file stream
if (false !== ($bytes = fwrite_stream($xmlstream, $xmlData))) {
$success[] = [
'filepath' => $filepath,
'remote_filepath' => $remote_filepath,
'bytes' => $bytes,
];
}
} finally {
//shortcut to ensure the xmlstream is closed on success or failure
if (isset($xmlstream) && is_resource($xmlstream)) {
fclose($xmlstream);
}
}
}
//testing purposes only to show the resulting uploads
if (!empty($success)) {
var_export($success);
}
} finally {
//shortcut to disconnect the ssh2 session on success or failure
$sftp = null;
unset($sftp);
if (isset($ssh2) && is_resource($ssh2)) {
ssh2_disconnect($ssh2);
}
}
/*
* Taken from PHP Manual
* Writing to a network stream may end before the whole string is written.
* Return value of fwrite() may be checked
*/
function fwrite_stream($fp, $string)
{
for ($written = 0, $writtenMax = strlen($string); $written < $writtenMax; $written += $fwrite) {
$fwrite = fwrite($fp, substr($string, $written));
if (false === $fwrite) {
return $written;
}
}
return $written;
}
NOTE
All file operations will be created using the
ssh2_auth_password
user as the owner/group. You must ensure the specified user has read and write access to the desired directories.Use the appropriate file masks to ensure desired file/directory permissions
0777
(default) allows everyone to read, write, execute!0750
is typically desired for directories0640
is typically desired for individual files- use
chmod($path, 0750)
to change permissions on the remote file(s)- use
chown($path, 'user')
to change the owner on the remote file(s)- use
chgrp($path, 'group')
to change the group on the remote file(s)
结果
C:\xml\Race_1\file1.xml size 9
C:\xml\Race_1\file2.xml size 11
array (
0 =>
array (
'filepath' => 'C:\xml\Race_1\file1.xml',
'remote_filepath' => 'ssh2.sftp://Resource id #5/FTP/XML/Race_1/file1.xml',
'bytes' => 9,
),
1 =>
array (
'filepath' => 'C:\xml\Race_1\file2.xml',
'remote_filepath' => 'ssh2.sftp://Resource id #5/FTP/XML/Race_1/file2.xml',
'bytes' => 11,
),
)