[教學] 完美將 Discuz 附件、會員頭像、靜態檔案外連至圖床(遠程附件)
最近在研究 Discuz 外連圖床的相關功能,除了官方提供的遠程附件,
還有會員頭像自動遠程、靜態檔案外連...等功能,
自己有先 Google 了一些問題,但發現網路上能找到的資訊屈指可數,
好像這些東西在 Discuz 不是一個很常見的功能,
就連官方論壇上面網友的發問,也沒有正確答案可以參考,
只好發揮自己研究的精神,來完成這些功能了!
適用版本:Discuz X3.x
Discuz 官方有提供遠程附件的功能,可以將帖內上傳的附件、圖片、圖片縮圖等等,自動傳到遠程伺服器上,
不過在手機版看到的圖片依舊本地圖片,下面也會一一提供解決方法。
開啟 Discuz 遠程附件
首先要先從後台開啟 Discuz 的遠程附件功能,進入全局>上傳設置>遠程附件,
啟用遠程附件選是,接著填入圖床的 FTP 登入資訊,伺服器、帳號、密碼等等,
要特別注意的是遠程附件目錄這個地方要填寫正確,這個目錄是拿來存放遠程檔案的,
如果你的 FTP 帳號登入後,要進入 public_html 或 htdocs 之類的資料夾才能上傳檔案,
那麼遠程目錄裡也要填入(訪問 URL 就不用),否則遠程測試會不成功,
這個部分很多人都沒注意到,所以測試都失敗,我自己一開始也忽略了,網路上也沒人給解答...
遠程訪問 URL 是實際連結的網址,要跟隨遠程目錄,不然會連不到檔案,
像我目錄是設定 public_html/data/attachment,所以訪問 URL 就填 http://圖床網址/data/attachment。
送出前可以先點擊 [測試遠程附件] 測試一下是否正常,若出現遠程附件設置一切正常,就可以放心的提交了。
若是全新的 Discuz 論壇,可以直接略過這步驟,若在開啟遠程附件之前已經有附件或圖片紀錄,
請先將本地伺服器(論壇主要伺服器)目錄下 /data/attachment 下的所有目錄先手動傳到你剛剛設定的遠程附件目錄下,
這樣過去所上傳的圖片、附件也能直接連結到圖床上。
到此,在帖子內上傳的附件、圖片都會自動傳到圖床,帖子顯示的圖片也是指向圖床網址了。
不過你可能也發現了,在手機板模式下,圖片的網址依舊是連結到本地的圖片阿!!
圖片上連結的網址是 forum.php?mod=image&aid=XXX&size=1120x1120&key=XXX&type=fixnone,
但實際訪問該連結,依舊是轉向本地的圖片,Discuz 官方論壇也有許多反饋的帖子,但官方的回應都是,不支援、不支援,就是不支援....
竟然官方不支援,那就只好自己修改了!
讓手機版圖片也連結到圖床
首先先以文字檔開啟論壇目錄下 source/module/forum/forum_image.php(修改前請先備份),
找到第 24 行:
dheader('location: '.$attachurl.$thumbfile);
在前面加上兩條斜線 //:
//dheader('location: '.$attachurl.$thumbfile);
接著找到第 53 行:
dheader('location: '.$attachurl.$thumbfile);
同樣在前面加兩條斜線 //:
//dheader('location: '.$attachurl.$thumbfile);
接著在同一行最後方按下 Enter 換新行,加入:
dheader('location: '.$filename);
保存,上傳回伺服器,這時就會發現圖片已經指向圖床瞜!
將會員頭像自動遠程到圖床
在 Discuz 論壇中,會員頭像無處不在,而且在同一頁中出現的數量都不少,
所以當初我就想說把頭像一起遠程,不過官方似乎沒有提供這個功能,
上網找也沒有人提供相關教學,因此這裡又要來動手修改文件啦!
需要修改到的文件
- uc_server/avatar.php
- uc_server/control/user.php
- source/function/function_core.php(若有開啟「使用靜態地址調用頭像」)
事前準備
(更新)為了讓遠程伺服器的資料夾和本機端同步,需要多做一些 FTP 操作,
請先將以下代碼全部複製,存為 ftp.php
,並傳到 uc_server/control/
下。
<?php
class Ftp {
//FTP 连接资源
private $link;
//FTP连接时间
public $link_time;
//错误代码
private $err_code = 0;
//传送模式{文本模式:FTP_ASCII, 二进制模式:FTP_BINARY}
public $mode = FTP_BINARY;
/**
* 连接FTP服务器
* @param string $host 服务器地址
* @param string $username 用户名
* @param string $password 密码
* @param integer $port 服务器端口,默认值为21
* @param boolean $pasv是否开启被动模式
* @param boolean $ssl 是否使用SSL连接
* @param integer $timeout 超时时间
*/
public function connect($host, $username = '', $password = '', $port = '21', $pasv = false, $ssl = false, $timeout = 60) {
$start = time();
if ($ssl) {
if (!$this->link = @ftp_ssl_connect($host, $port, $timeout)) {
$this->err_code = 1;
return false;
}
} else {
if (!$this->link = @ftp_connect($host, $port, $timeout)) {
$this->err_code = 1;
return false;
}
}
if (@ftp_login($this->link, $username, $password)) {
if ($pasv)
ftp_pasv($this->link, true);
$this->link_time = time() - $start;
return true;
} else {
$this->err_code = 1;
return false;
}
register_shutdown_function(array(&$this, 'close'));
}
/**
* 创建文件夹
* @param string $dirname 目录名,
*/
public function mkdir($dirname) {
if (!$this->link) {
$this->err_code = 2;
return false;
}
$dirname = $this->ck_dirname($dirname);
$nowdir = '/';
foreach ($dirname as $v) {
if ($v && !$this->chdir($nowdir . $v)) {
if ($nowdir)
$this->chdir($nowdir);
@ftp_mkdir($this->link, $v);
}
if ($v)
$nowdir .= $v . '/';
}
return true;
}
/**
* 上传文件
* @param string $remote 远程存放地址
* @param string $local 本地存放地址
*/
public function put($remote, $local) {
if (!$this->link) {
$this->err_code = 2;
return false;
}
$dirname = pathinfo($remote, PATHINFO_DIRNAME);
if (!$this->chdir($dirname)) {
$this->mkdir($dirname);
}
if (@ftp_put($this->link, $remote, $local, $this->mode)) {
return true;
} else {
$this->err_code = 7;
return false;
}
}
/**
* 删除文件夹
* @param string $dirname目录地址
* @param boolean $enforce 强制删除
*/
public function rmdir($dirname, $enforce = false) {
if (!$this->link) {
$this->err_code = 2;
return false;
}
$list = $this->nlist($dirname);
if ($list && $enforce) {
$this->chdir($dirname);
foreach ($list as $v) {
$this->f_delete($v);
}
} elseif ($list && !$enforce) {
$this->err_code = 3;
return false;
}
@ftp_rmdir($this->link, $dirname);
return true;
}
/**
* 删除指定文件
* @param string $filename 文件名
*/
public function f_delete($filename) {
if (!$this->link) {
$this->err_code = 2;
return false;
}
if (@ftp_delete($this->link, $filename)) {
return true;
} else {
$this->err_code = 4;
return false;
}
}
/**
* 返回给定目录的文件列表
* @param string $dirname目录地址
* @return array 文件列表数据
*/
public function nlist($dirname) {
if (!$this->link) {
$this->err_code = 2;
return false;
}
if ($list = @ftp_nlist($this->link, $dirname)) {
return $list;
} else {
$this->err_code = 5;
return false;
}
}
/**
* 在 FTP 服务器上改变当前目录
* @param string $dirname 修改服务器上当前目录
*/
public function chdir($dirname) {
if (!$this->link) {
$this->err_code = 2;
return false;
}
if (@ftp_chdir($this->link, $dirname)) {
return true;
} else {
$this->err_code = 6;
return false;
}
}
/**
* 获取错误信息
*/
public function get_error() {
if (!$this->err_code)
return false;
$err_msg = array(
'1' => 'Server can not connect',
'2' => 'Not connect to server',
'3' => 'Can not delete non-empty folder',
'4' => 'Can not delete file',
'5' => 'Can not get file list',
'6' => 'Can not change the current directory on the server',
'7' => 'Can not upload files'
);
return $err_msg[$this->err_code];
}
/**
* 检测目录名
* @param string $url 目录
* @return 由 / 分开的返回数组
*/
private function ck_dirname($url) {
$url = str_replace('', '/', $url);
$urls = explode('/', $url);
return $urls;
}
/**
* 关闭FTP连接
*/
public function close() {
return @ftp_close($this->link);
}
}
?>
(程式碼來自:http://blog.csdn.net/yanhui_wei/article/details/17616171)
開始修改文件
首先先修改 user.php
,這裡要修改的內容是,當用戶更換頭像時,要自動同步到圖床去。
找到第 357 行:
@fclose($fp);
在下方加上:
/*
Discuz 自動遠程頭像
*/
include 'ftp.php';
$ftps = new Ftp();
$dir = '遠程目錄';
// 連結 FTP 伺服器
$ftps->connect('伺服器位置', 'FTP帳號', 'FTP密碼', '21', true);
// 上傳檔案
$ftps->put($dir.$this->get_avatar($uid, 'big', $avatartype), $bigavatarfile);
$ftps->put($dir.$this->get_avatar($uid, 'middle', $avatartype), $middleavatarfile);
$ftps->put($dir.$this->get_avatar($uid, 'small', $avatartype), $smallavatarfile);
// 關閉連線
$ftps->close();
說明:
需自行填入圖床的伺服器位置、FTP 帳號、FTP 密碼、遠程目錄,類似剛剛遠程附件填寫的內容,
遠程目錄是你要存放頭像的位置,建議放在 ./data/avatar,注意這裡填寫的目錄,也要注意是否有 public_html、htdocs 等目錄存在。
修改完畢存檔上傳,如此一來當用戶更換頭像時,除了本地會跟著新增及更換外,圖床上的圖片也會一起更新了!
修改文件後,記得先將本地伺服器目錄 uc_server/data/avatar 下的所有檔案(都是會員頭像),移動到圖床上,注意目錄要和上面填寫的相同。
接下來繼續修改 avatar.php
,這是調用頭像網址的文件。
找到第 21 行:
$avatar = './data/avatar/'.get_avatar($uid, $size, $type);
若你剛剛設定的遠程目錄不是 ./data/avatar(路徑前綴為 public_html、htdocs 等等除外),這裡的 './data/avatar/' 就要自行更改,
接著在下方加上:
$remoteURL = '圖床網址';
這裡的圖床網址是要實際連結到圖片的網址,必須以 http:// 開頭,不必填寫目錄名,
接著找到第 23 行:
if(file_exists(dirname(__FILE__).'/'.$avatar)) {
修改為:
if(remote_file_exists($remoteURL.$avatar)) {
接著找到第 45 行:
header('Location: '.UC_API.'/'.$avatar_url);
修改為:
header('Location: '.$remoteURL.$avatar_url);
最後找到最後一行:
?>
在前一行加上:
function remote_file_exists($file) { // 檢測遠程文件是否存在
if(preg_match('/^http:\/\//',$file)) {
if(ini_get('allow_url_fopen')) {
if(@fopen($file, 'r')) return true;
}
else {
$parseurl = parse_url($file);
$host = $parseurl['host'];
$path = $parseurl['path'];
$fp = fsockopen($host,80, $errno, $errstr, 10);
if(!$fp) return false;
fputs($fp,"GET {$path} HTTP/1.1 \r\nhost:{$host}\r\n\r\n");
if(preg_match('/HTTP\/1.1 200/', fgets($fp, 1024))) return true;
}
return false;
}
return file_exists($file);
}
修改完後保存上傳,這樣論壇上顯示的頭像就都連結到圖床了!
不過若你有開啟「使用靜態地址調用頭像」這功能,要額外修改 function_core.php
,讓靜態網址也指向圖床,
開啟檔案,找到第 426 行:
$file = $ucenterurl.'/data/avatar/'.$dir1.'/'.$dir2.'/'.$dir3.'/'.substr($uid, -2).($real ? '_real' : '').'_avatar_'.$size.'.jpg';
修改為:
//$file = $ucenterurl.'/data/avatar/'.$dir1.'/'.$dir2.'/'.$dir3.'/'.substr($uid, -2).($real ? '_real' : '').'_avatar_'.$size.'.jpg';
接著換新行,加上:
$file = '目錄連結網址'.$dir1.'/'.$dir2.'/'.$dir3.'/'.substr($uid, -2).($real ? '_real' : '').'_avatar_'.$size.'.jpg?';
目錄連結網址為圖床網址 + 頭像存放目錄,像以剛剛建議的目錄來說,就是填寫 http://圖床網址/data/avatar/。
修改完畢後保存上傳,到此,遠程頭像的功能已經大功告成啦~
將靜態目錄移動到圖床上
最後要將靜態目錄(static/)移動到遠程伺服器上,
首先將本地伺服器 static/ 目錄下所有的檔案移動到圖床上,
建議也是放到根目錄的 static 資料夾內。
接著開啟 config/config_global.php
,
找到第 48行:
$_config['output']['staticurl'] = 'static/';
修改為:
$_config['output']['staticurl'] = 'http://圖床網址/static目錄名稱/';
將引號裡的內容修改為你剛剛上傳到圖床的目錄連結即可,修改完畢後保存上傳。
接著到 Discuz 後台全局>性能優化>服務器優化,找到 JS 文件目錄,
選擇自定義目錄,後方填寫 http://圖床網址/static目錄名稱/js,提交後即可。
編輯完所有文件後,記得先到後台工具>更新緩存清理一下快取,
這樣一來,所有的遠程工作就已經告一段落了,
但可能有些文件沒辦法完美遠程,要自己手動一一去修改,這裡就不額外提了,
不過這樣應該已經能大幅的降低論壇消耗的資源了!
以上就是本次分享的教學,若有任何問題歡迎提出討論~
carry
貌似不行呢....我是x3.2的,我按測試後一直出現訪問url有問題..
carry
貌似不行呢...我設置後一直出現訪問url有問題...
測試版本:X3.2 TC UTF-8
Lay
麻煩提出具體的問題喔,哪個步驟出了問題呢?
後台的遠程附件測試正常嗎?
PP
SFTP好像無法使用 :cry: :cry: :cry:
DZ3.2
Lay
嗨,沒錯 Discuz 只支援一般 FTP,不支援 SSH~
Lazer
我也是不行 不知到會不會跟主機不支援遠程附件有關係呢?
Lay
基本上可以使用 FTP 登入的主機都可以當遠程伺服器喔~
Lazer
那請問有無可以私下可以與你聯絡的方式呢? 想請你幫我測試看看是哪一個環節有錯誤 導致我無法遠程附件測試成功 謝謝
Lay
可以直接在這裡回應,我會回覆喔~
測試時出現錯誤訊息是?
路過
您好,請問一下,您說的手機瀏覽的一樣還是本地圖片,是指原本就存在的本地圖片上傳到遠程?還是指設定遠程之後,新上傳的圖片?
Lay
遠程附件會在附件上傳到本地主機後,透過 FTP 複製一份到遠程主機(所以會有兩份),
電腦版顯示的是遠程主機上的圖片(靜態圖片網址),手機版是顯示本地主機的圖片(forum.php?mod=image&aid=xxxx),
這是預設值,如果要修改可能就要從 Discuz 源碼下手~
路過
謝謝您回覆,要早點休息哦~ :laughing:
所以遠程附件是兩邊都上傳,電腦上網讀的是遠程的附件,是這樣吧?
我以為是全部傳到遠程了...
Lay
對,本地會保留一份
不會程式的人
您好,請問一下,附件上傳如果不使用縮略圖,而是開啟直接把圖縮小...
那麼,
圖片是在上傳之前被電腦端或手機端壓縮後,才上傳?
還是上傳之後,由租用的主機端做壓縮縮圖的動作?
Lay
不太懂你的意思,Discuz 好像沒有只壓縮圖片的功能吧?
只有要不要產生略縮圖的選項,如果不產生略縮圖那就是直接保存原圖。
不會程式的人
我是開啟「直接縮放原圖」,上傳後,附件不留原圖,只有縮小後的圖,主機存放的資料夾(data/attachment)裡也找不到原圖,只有縮小後的圖。
所以,不知道是怎麼壓縮的...,如果是上傳到主機才壓縮,怕吃流量...。如果電腦或手機端壓縮後才上傳,就不擔心流量了...
Lay
那可能就要針對這部分直接改 Discuz 本身的系統文件了。
Calvin
您好,v3.4-20191201 版本中的 avatar.php 有更改過代碼,導致第 23 行找不到,請問這部份要怎麼修改呢?謝謝。
Lay
現在應該在第 27 行:
if(file_exists($avatar_file)) {
更改為:
if(remote_file_exists($remoteURL.$avatar)) {
Calvin
感謝回覆,已設置成功。
另外還有個問題想請教的,我本來是使用 X3.2 版本,設置 HTTPS 都正常的。
最近升級到 X3.4 之後,頭像那邊依然使用 HTTP,網絡搜尋得知改成「使用靜態地址調用頭像」就正常了,
但是又變了第二個問題出現......就是如果會員沒有上傳頭像的話都會顯示死圖,並沒有調用到顯示默認頭像,
而我把頭像改成遠程之後,查看沒有上傳頭像的會員連結,發現變成 http://圖床/data/avatar/000/00/25/19_avatar_big.jpg
可是他就沒有上傳頭像呀,這個連結是404也是正常,請問要怎樣才能正常顯示默認頭像呢? :thinking:
Lay
1. http 的問題應該要找一下跟 avatar 有關的函式,可能是 ucenter url 沒有改成 https
2. 404 的問題可以用
onerror="this.src=''"
這種方式讓無法載入的圖片載入預設圖,可以參考文中function_core.php
的avatar()
最後一行$returnsrc
Calvin
頭像的 HTTPS 問題終於解決了,原來 avatar.php 有一處需要更改。
不過我再次依教學設置,還是不成功,在頭像使用動態地址的情況下,設置完之後全部人的頭像都變成預設的,
而在靜態地址的情況下,依然像上面所說的,預設頭像變成死圖,已經參考了 function_core.php $returnsrc 的改法。
看來我要放棄把頭像設成遠程了,不過還是感謝 Lay 耐心回覆。 :laughing: :thumbsup: