在开发中应该如何上传到另一台服务器?
常见解决方案有:
1、虚拟主机等权限低的,一般在上传成功或者图片处理完成以后通过ftp协议等把文件推到其它服务器上
2、上传地址随机生成,指向到不同的服务器,以达到每个服务器上传压力和存储负载均衡
3、上传服务器上有事件监听事件,有新文件上传,可以使用rsync等协议将文件推到其它服务器
4、通过CDN等方式在有需要的时候,将文件同步到其它服务器
在使用php开发中可以通过用ftp函数集完成将图片推到其他服务器上
存放文件的服务器需要安装ftp服务,参考ubuntu14.10安装vsftpd与本地用户配置
以下CI框架的FTP类,该类封装就标准的FTP操作,稍微修改了一下具体使用可以参考ci文档
http://codeigniter.com/user_guide/libraries/ftp.html
<?php
/**
* CodeIgniter
*
* An open source application development framework for PHP 5.1.6 or newer
*
* @package CodeIgniter
* @author EllisLab Dev Team
* @copyright Copyright (c) 2008 - 2014, EllisLab, Inc.
* @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/)
* @license http://codeigniter.com/user_guide/license.html
* @link http://codeigniter.com
* @since Version 1.0
* @filesource
*/
// ------------------------------------------------------------------------
/**
* FTP Class
*
* @package CodeIgniter
* @subpackage Libraries
* @category Libraries
* @author EllisLab Dev Team
* @link http://codeigniter.com/user_guide/libraries/ftp.html
*/
class CI_FTP {
private $hostname = '';
private $username = '';
private $password = '';
private $port = 21;
private $passive = TRUE;
private $debug = FALSE;
private $conn_id = FALSE;
/**
* Constructor - Sets Preferences
*
* The constructor can be passed an array of config values
*/
public function __construct($config = array()) {
if (count($config) > 0) {
$this->initialize($config);
}
// log_message('debug', "FTP Class Initialized");
}
// --------------------------------------------------------------------
/**
* Initialize preferences
*
* @access public
* @param array
* @return void
*/
private function initialize($config = array()) {
foreach ($config as $key => $val) {
if (isset($this->$key)) {
$this->$key = $val;
}
}
// Prep the hostname
$this->hostname = preg_replace('|.+?://|', '', $this->hostname);
}
// --------------------------------------------------------------------
/**
* FTP Connect
*
* @access public
* @param array the connection values
* @return bool
*/
public function connect($config = array()) {
if (count($config) > 0) {
$this->initialize($config);
}
if (FALSE === ($this->conn_id = @ftp_connect($this->hostname, $this->port))) {
if ($this->debug == TRUE) {
$this->_error('ftp_unable_to_connect');
}
return FALSE;
}
if (!$this->_login()) {
if ($this->debug == TRUE) {
$this->_error('ftp_unable_to_login');
}
return FALSE;
}
// Set passive mode if needed
if ($this->passive == TRUE) {
ftp_pasv($this->conn_id, TRUE);
}
return TRUE;
}
// --------------------------------------------------------------------
/**
* FTP Login
*
* @access private
* @return bool
*/
private function _login() {
return @ftp_login($this->conn_id, $this->username, $this->password);
}
// --------------------------------------------------------------------
/**
* Validates the connection ID
*
* @access private
* @return bool
*/
private function _is_conn() {
if (!is_resource($this->conn_id)) {
if ($this->debug == TRUE) {
$this->_error('ftp_no_connection');
}
return FALSE;
}
return TRUE;
}
// --------------------------------------------------------------------
/**
* Change directory
*
* The second parameter lets us momentarily turn off debugging so that
* this function can be used to test for the existence of a folder
* without throwing an error. There's no FTP equivalent to is_dir()
* so we do it by trying to change to a particular directory.
* Internally, this parameter is only used by the "mirror" function below.
*
* @access public
* @param string
* @param bool
* @return bool
*/
public function changedir($path = '', $supress_debug = FALSE) {
if ($path == '' OR ! $this->_is_conn()) {
return FALSE;
}
$result = @ftp_chdir($this->conn_id, $path);
if ($result === FALSE) {
if ($this->debug == TRUE AND $supress_debug == FALSE) {
$this->_error('ftp_unable_to_changedir');
}
return FALSE;
}
return TRUE;
}
// --------------------------------------------------------------------
/**
* Create a directory
*
* @access public
* @param string
* @return bool
*/
public function mkdir($path = '', $permissions = NULL) {
if ($path == '' OR ! $this->_is_conn()) {
return FALSE;
}
$result = @ftp_mkdir($this->conn_id, $path);
if ($result === FALSE) {
if ($this->debug == TRUE) {
$this->_error('ftp_unable_to_makdir');
}
return FALSE;
}
// Set file permissions if needed
if (!is_null($permissions)) {
$this->chmod($path, (int) $permissions);
}
return TRUE;
}
// --------------------------------------------------------------------
/**
* Upload a file to the server
*
* @access public
* @param string
* @param string
* @param string
* @return bool
*/
public function upload($locpath, $rempath, $mode = 'auto', $permissions = NULL) {
if (!$this->_is_conn()) {
return FALSE;
}
if (!file_exists($locpath)) {
$this->_error('ftp_no_source_file');
return FALSE;
}
// Set the mode if not specified
if ($mode == 'auto') {
// Get the file extension so we can set the upload type
$ext = $this->_getext($locpath);
$mode = $this->_settype($ext);
}
$mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
$result = @ftp_put($this->conn_id, $rempath, $locpath, $mode);
if ($result === FALSE) {
if ($this->debug == TRUE) {
$this->_error('ftp_unable_to_upload');
}
return FALSE;
}
// Set file permissions if needed
if (!is_null($permissions)) {
$this->chmod($rempath, (int) $permissions);
}
return TRUE;
}
// --------------------------------------------------------------------
/**
* Download a file from a remote server to the local server
*
* @access public
* @param string
* @param string
* @param string
* @return bool
*/
public function download($rempath, $locpath, $mode = 'auto') {
if (!$this->_is_conn()) {
return FALSE;
}
// Set the mode if not specified
if ($mode == 'auto') {
// Get the file extension so we can set the upload type
$ext = $this->_getext($rempath);
$mode = $this->_settype($ext);
}
$mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
$result = @ftp_get($this->conn_id, $locpath, $rempath, $mode);
if ($result === FALSE) {
if ($this->debug == TRUE) {
$this->_error('ftp_unable_to_download');
}
return FALSE;
}
return TRUE;
}
// --------------------------------------------------------------------
/**
* Rename (or move) a file
*
* @access public
* @param string
* @param string
* @param bool
* @return bool
*/
public function rename($old_file, $new_file, $move = FALSE) {
if (!$this->_is_conn()) {
return FALSE;
}
$result = @ftp_rename($this->conn_id, $old_file, $new_file);
if ($result === FALSE) {
if ($this->debug == TRUE) {
$msg = ($move == FALSE) ? 'ftp_unable_to_rename' : 'ftp_unable_to_move';
$this->_error($msg);
}
return FALSE;
}
return TRUE;
}
// --------------------------------------------------------------------
/**
* Move a file
*
* @access public
* @param string
* @param string
* @return bool
*/
public function move($old_file, $new_file) {
return $this->rename($old_file, $new_file, TRUE);
}
// --------------------------------------------------------------------
/**
* Rename (or move) a file
*
* @access public
* @param string
* @return bool
*/
public function delete_file($filepath) {
if (!$this->_is_conn()) {
return FALSE;
}
$result = @ftp_delete($this->conn_id, $filepath);
if ($result === FALSE) {
if ($this->debug == TRUE) {
$this->_error('ftp_unable_to_delete');
}
return FALSE;
}
return TRUE;
}
// --------------------------------------------------------------------
/**
* Delete a folder and recursively delete everything (including sub-folders)
* containted within it.
*
* @access public
* @param string
* @return bool
*/
public function delete_dir($filepath) {
if (!$this->_is_conn()) {
return FALSE;
}
// Add a trailing slash to the file path if needed
$filepath = preg_replace("/(.+?)\/*$/", "\\1/", $filepath);
$list = $this->list_files($filepath);
if ($list !== FALSE AND count($list) > 0) {
foreach ($list as $item) {
// If we can't delete the item it's probaly a folder so
// we'll recursively call delete_dir()
if (!@ftp_delete($this->conn_id, $item)) {
$this->delete_dir($item);
}
}
}
$result = @ftp_rmdir($this->conn_id, $filepath);
if ($result === FALSE) {
if ($this->debug == TRUE) {
$this->_error('ftp_unable_to_delete');
}
return FALSE;
}
return TRUE;
}
// --------------------------------------------------------------------
/**
* Set file permissions
*
* @access public
* @param string the file path
* @param string the permissions
* @return bool
*/
public function chmod($path, $perm) {
if (!$this->_is_conn()) {
return FALSE;
}
if (!function_exists('ftp_chmod')) {
if ($this->debug == TRUE) {
$this->_error('ftp_unable_to_chmod');
}
return FALSE;
}
$result = @ftp_chmod($this->conn_id, $perm, $path);
if ($result === FALSE) {
if ($this->debug == TRUE) {
$this->_error('ftp_unable_to_chmod');
}
return FALSE;
}
return TRUE;
}
// --------------------------------------------------------------------
/**
* FTP List files in the specified directory
*
* @access public
* @return array
*/
public function list_files($path = '.') {
if (!$this->_is_conn()) {
return FALSE;
}
return ftp_nlist($this->conn_id, $path);
}
// ------------------------------------------------------------------------
/**
* Read a directory and recreate it remotely
*
* This function recursively reads a folder and everything it contains (including
* sub-folders) and creates a mirror via FTP based on it. Whatever the directory structure
* of the original file path will be recreated on the server.
*
* @access public
* @param string path to source with trailing slash
* @param string path to destination - include the base folder with trailing slash
* @return bool
*/
public function mirror($locpath, $rempath) {
if (!$this->_is_conn()) {
return FALSE;
}
// Open the local file path
if ($fp = @opendir($locpath)) {
// Attempt to open the remote file path.
if (!$this->changedir($rempath, TRUE)) {
// If it doesn't exist we'll attempt to create the direcotory
if (!$this->mkdir($rempath) OR ! $this->changedir($rempath)) {
return FALSE;
}
}
// Recursively read the local directory
while (FALSE !== ($file = readdir($fp))) {
if (@is_dir($locpath . $file) && substr($file, 0, 1) != '.') {
$this->mirror($locpath . $file . "/", $rempath . $file . "/");
} elseif (substr($file, 0, 1) != ".") {
// Get the file extension so we can se the upload type
$ext = $this->_getext($file);
$mode = $this->_settype($ext);
$this->upload($locpath . $file, $rempath . $file, $mode);
}
}
return TRUE;
}
return FALSE;
}
// --------------------------------------------------------------------
/**
* Extract the file extension
*
* @access private
* @param string
* @return string
*/
private function _getext($filename) {
if (FALSE === strpos($filename, '.')) {
return 'txt';
}
$x = explode('.', $filename);
return end($x);
}
// --------------------------------------------------------------------
/**
* Set the upload type
*
* @access private
* @param string
* @return string
*/
private function _settype($ext) {
$text_types = array(
'txt',
'text',
'php',
'phps',
'php4',
'js',
'css',
'htm',
'html',
'phtml',
'shtml',
'log',
'xml'
);
return (in_array($ext, $text_types)) ? 'ascii' : 'binary';
}
// ------------------------------------------------------------------------
/**
* Close the connection
*
* @access public
* @param string path to source
* @param string path to destination
* @return bool
*/
public function close() {
if (!$this->_is_conn()) {
return FALSE;
}
@ftp_close($this->conn_id);
}
// ------------------------------------------------------------------------
/**
* Display error message
*
* @access private
* @param string
* @return bool
*/
private function _error($line) {
try {
throw new Exception($line);
} catch (Exception $exc) {
echo $exc->getMessage();
}
}
}
使用例子
<?php
include 'Ftp.php';
$ftp_conf = [
'hostname'=>'10.5.26.134',
'username'=>'ftpsite',
'password'=>'123123',
];
$ftp = new CI_FTP();
$ftp->connect($ftp_conf);
$rs = $ftp->upload('ax.js','assets/aa.js');
var_dump($rs);
$ftp->close();
ubuntu14.10安装vsftpd与本地用户配置
安装:
sudo apt-get install vsftpd
配置:vsftpd.conf
listen=NO
listen_ipv6=YES
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022
dirmessage_enable=YES
use_localtime=YES
xferlog_enable=YES
connect_from_port_20=YES
ftpd_banner=Welcome to blah FTP service.
chroot_local_user=YES
allow_writeable_chroot=YES
secure_chroot_dir=/var/run/vsftpd/empty
pam_service_name=vsftpd
rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
ssl_enable=NO
userlist_enable=YES
userlist_deny=NO
userlist_file=/etc/vsftpd.allowuser
配置后重新启动下服务
sudo service vsftpd restart
创建本地用户帐号供ftp登录使用
添加用户并设置home目录
sudo useradd -d /home/ftpsite/ -s /usr/sbin/nologin ftpsite
设置密码
sudo passwd ftpsite
配置允许登录到ftp文件vsftpd.allowuser
ftpsite
登录是可能出现下面错误
[右] 331 Please specify the password.
[右] PASS (hidden)
[右] 530 Login incorrect.
[右] 连接失败
可以试下注释/etc/pam.d/vsftpd文件中的auth required pam_shells.so
当我们限定了用户不能跳出其主目录之后,即设置chroot_local_user=YES
使用该用户登录FTP时往往会遇到这个错误:
500 OOPS: vsftpd: refusing to run with writable root inside chroot()
这个问题发生在最新的这是由于下面的更新造成的
从2.3.5之后,vsftpd增强了安全检查,如果用户被限定在了其主目录下,则该用户的主目录不能再具有写权限了!如果检查发现还有写权限,就会报该错误。
要修复这个错误,可以用命令chmod a-w /home/user去除用户主目录的写权限,注意把目录替换成你自己的。或者你可以在vsftpd的配置文件中增加:allow_writeable_chroot=YES