安装Docker

[root@localhost ~]# dnf -y install dnf-plugins-core
[root@localhost ~]# dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
[root@localhost ~]# dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
[root@localhost ~]# systemctl enable --now docker
[root@localhost ~]# yum update
[root@localhost ~]# yum install docker-compose-plugin

构建Hexo容器基础镜像

[root@localhost ~]# docker pull node
[root@localhost ~]# vi Dockerfile
# 将以下内容写入在Dockerfile中
FROM node:latest
MAINTAINER Shadowyingyan
EXPOSE 4000
WORKDIR /hexo
RUN npm install hexo-cli -g
RUN hexo init .
RUN npm install
CMD ["hexo", "server"]
# 文件截止
[root@localhost ~]# docker build -t 'hexo:latest' .
[+] Building 104.4s (9/9) FINISHED                                                                                                             docker:default
 => [internal] load build definition from Dockerfile                                                                                                     0.0s
 => => transferring dockerfile: 250B                                                                                                                     0.0s
 => WARN: MaintainerDeprecated: Maintainer instruction is deprecated in favor of using label (line 3)                                                    0.0s
 => [internal] load metadata for docker.io/library/node:latest                                                                                           0.0s
 => [internal] load .dockerignore                                                                                                                        0.0s
 => => transferring context: 2B                                                                                                                          0.0s
 => [1/5] FROM docker.io/library/node:latest                                                                                                             0.0s
 => [2/5] WORKDIR /hexo                                                                                                                                  0.0s
 => [3/5] RUN npm install hexo-cli -g                                                                                                                   20.2s
 => [4/5] RUN hexo init .                                                                                                                               81.3s
 => [5/5] RUN npm install                                                                                                                                1.6s 
 => exporting to image                                                                                                                                   1.3s 
 => => exporting layers                                                                                                                                  1.3s 
 => => writing image sha256:7b3f0e7f46c16a6b05d1202bff3041240c64c580c78ee1a9ebf15569f8fe951b                                                             0.0s 
 => => naming to docker.io/library/hexo:latest                                                                                                                0.0s 
[root@localhost ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
hexo         latest    7b3f0e7f46c1   3 minutes ago   1.24GB
node         latest    71e2b415fa05   3 days ago      1.12GB

测试Hexo基础镜像

[root@localhost ~]# docker run -it --name="temp" -p 4000:4000  hexo:latest
INFO  Validating config
INFO  Start processing
INFO  Hexo is running at http://localhost:4000/ . Press Ctrl+C to stop.

image-gWJi.png

使用浏览器访问虚拟机的IP地址加4000端口可以访问到Hexo基础容器。

编写Owncloud容器的编排内容

[root@localhost ~]# mkdir owncloud-docker-server
[root@localhost owncloud-docker-server]# vi docker-compose.yml
# 将以下内容写入到文本中
version: "3"

volumes:
  mysql:
    driver: local
  redis:
    driver: local

services:
  owncloud:
    image: owncloud/server:${OWNCLOUD_VERSION}
    container_name: owncloud_server
    restart: always
    ports:
      - ${HTTP_PORT}:8080
    depends_on:
      - mariadb
      - redis
    environment:
      - OWNCLOUD_DOMAIN=${OWNCLOUD_DOMAIN}
      - OWNCLOUD_TRUSTED_DOMAINS=${OWNCLOUD_TRUSTED_DOMAINS}
      - OWNCLOUD_DB_TYPE=mysql
      - OWNCLOUD_DB_NAME=owncloud
      - OWNCLOUD_DB_USERNAME=owncloud
      - OWNCLOUD_DB_PASSWORD=owncloud
      - OWNCLOUD_DB_HOST=mariadb
      - OWNCLOUD_ADMIN_USERNAME=${ADMIN_USERNAME}
      - OWNCLOUD_ADMIN_PASSWORD=${ADMIN_PASSWORD}
      - OWNCLOUD_MYSQL_UTF8MB4=true
      - OWNCLOUD_REDIS_ENABLED=true
      - OWNCLOUD_REDIS_HOST=redis
    healthcheck:
      test: ["CMD", "/usr/bin/healthcheck"]
      interval: 30s
      timeout: 10s
      retries: 5
    volumes:
      - /data/owncloud/file:/mnt/data

  mariadb:
    image: mariadb:10.11 # minimum required ownCloud version is 10.9
    container_name: owncloud_mariadb
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=owncloud
      - MYSQL_USER=owncloud
      - MYSQL_PASSWORD=owncloud
      - MYSQL_DATABASE=owncloud
      - MARIADB_AUTO_UPGRADE=1
    command: ["--max-allowed-packet=128M", "--innodb-log-file-size=64M"]
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-u", "root", "--password=owncloud"]
      interval: 10s
      timeout: 5s
      retries: 5
    volumes:
      - mysql:/var/lib/mysql

  redis:
    image: redis:6
    container_name: owncloud_redis
    restart: always
    command: ["--databases", "1"]
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    volumes:
      - redis:/data
# 文件结束
[root@localhost owncloud-docker-server]# vi .env 
# 将以下内容添加到文件中
OWNCLOUD_VERSION=10.15
OWNCLOUD_DOMAIN=localhost:8080
OWNCLOUD_TRUSTED_DOMAINS=192.168.17.128
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin
HTTP_PORT=8080
# 文件结束
[root@localhost owncloud-docker-server]# docker compose up -d
WARN[0000] /root/owncloud-docker-server/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion 
[+] Running 4/4
 ✔ Network owncloud-docker-server_default  Created                                                                                                       0.0s 
 ✔ Container owncloud_redis                Started                                                                                                       0.2s 
 ✔ Container owncloud_mariadb              Started                                                                                                       0.2s 
 ✔ Container owncloud_server               Started                                                                                                       0.3s 
[root@localhost owncloud-docker-server]# 

使用浏览器访问虚拟机IP地址加8080端口发现可以访问到Owncloud页面。

编写hexo+owncloud整合编排文件

现在需要将Hexo容器的内容和Owncloud容器的内容整合到一个docker-compose.yml文件中。

[root@localhost ~]# mkdir hexo-owncloud
[root@localhost ~]# cp owncloud-docker-server/.env hexo-owncloud/.
[root@localhost ~]# cd hexo-owncloud/
[root@localhost hexo-owncloud]# vi docker-compose.yml
# 将以下内容写入到文件中
version: "3"

volumes:
  mysql:
    driver: local
  redis:
    driver: local

services:
  hexo:
    image: hexo:latest
    container_name: hexo_server
    restart: always
    ports:
      - 80:4000
    volumes:
      - /data/hexo/:/hexo/
    tty: true
    command: ["tail", "-f", "/dev/null"]

  owncloud:
    image: owncloud/server:${OWNCLOUD_VERSION}
    container_name: owncloud_server
    restart: always
    ports:
      - ${HTTP_PORT}:8080
    depends_on:
      - mariadb
      - redis
    environment:
      - OWNCLOUD_DOMAIN=${OWNCLOUD_DOMAIN}
      - OWNCLOUD_TRUSTED_DOMAINS=${OWNCLOUD_TRUSTED_DOMAINS}
      - OWNCLOUD_DB_TYPE=mysql
      - OWNCLOUD_DB_NAME=owncloud
      - OWNCLOUD_DB_USERNAME=owncloud
      - OWNCLOUD_DB_PASSWORD=owncloud
      - OWNCLOUD_DB_HOST=mariadb
      - OWNCLOUD_ADMIN_USERNAME=${ADMIN_USERNAME}
      - OWNCLOUD_ADMIN_PASSWORD=${ADMIN_PASSWORD}
      - OWNCLOUD_MYSQL_UTF8MB4=true
      - OWNCLOUD_REDIS_ENABLED=true
      - OWNCLOUD_REDIS_HOST=redis
    healthcheck:
      test: ["CMD", "/usr/bin/healthcheck"]
      interval: 30s
      timeout: 10s
      retries: 5
    volumes:
      - /data/owncloud/passwd:/etc/passwd
      - /data/owncloud/file/:/mnt/data

  mariadb:
    image: mariadb:10.11 # minimum required ownCloud version is 10.9
    container_name: owncloud_mariadb
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=owncloud
      - MYSQL_USER=owncloud
      - MYSQL_PASSWORD=owncloud
      - MYSQL_DATABASE=owncloud
      - MARIADB_AUTO_UPGRADE=1
    command: ["--max-allowed-packet=128M", "--innodb-log-file-size=64M"]
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-u", "root", "--password=owncloud"]
      interval: 10s
      timeout: 5s
      retries: 5
    volumes:
      - mysql:/var/lib/mysql

  redis:
    image: redis:6
    container_name: owncloud_redis
    restart: always
    command: ["--databases", "1"]
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    volumes:
      - redis:/data
# 文件结束

创建hexo的文件夹目录

[root@localhost ~]# mkdir /data/hexo
[root@localhost ~]# chmod 777 /data/hexo

创建passwd文件内容,使www-data用户有权限操作owncloud容器内部内容。

[root@localhost ~]# vi /data/owncloud/passwd 
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/bin/bash
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin

删除刚刚测试的内容

[root@localhost ~]# cd owncloud-docker-server/
[root@localhost owncloud-docker-server]# docker compose down 
WARN[0000] /root/owncloud-docker-server/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion 
[+] Running 4/4
 ✔ Container owncloud_server               Removed                                                                                                      10.1s 
 ✔ Container owncloud_redis                Removed                                                                                                       0.1s 
 ✔ Container owncloud_mariadb              Removed                                                                                                       0.3s 
 ✔ Network owncloud-docker-server_default  Removed   
[root@localhost owncloud-docker-server]# docker volume ls
DRIVER    VOLUME NAME
local     owncloud-docker-server_mysql
local     owncloud-docker-server_redis
[root@localhost owncloud-docker-server]# rm -rf /data/owncloud/file/*
[root@localhost owncloud-docker-server]# docker volume rm owncloud-docker-server_mysql owncloud-docker-server_redis
owncloud-docker-server_mysql
owncloud-docker-server_redis
[root@localhost hexo-owncloud]# docker rm temp
temp

运行Docker compose编排文件

[root@localhost ~]# cd hexo-owncloud/
[root@localhost hexo-owncloud]# docker compose up -d
WARN[0000] /root/hexo-owncloud/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion 
[+] Running 4/4
 ✔ Container hexo_server       Running                                                                                                                   0.0s 
 ✔ Container owncloud_redis    Running                                                                                                                   0.0s 
 ✔ Container owncloud_mariadb  Running                                                                                                                   0.0s 
 ✔ Container owncloud_server   Running                                                                                                                   0.0s 

进入Hexo容器初始化Hexo文件夹

[root@localhost hexo-owncloud]# docker exec -it hexo_server bash
root@e6c4bceb9695:/hexo# hexo init

创建owncloud上的文件夹

访问owncloud,创建文件夹hexo_posts用来存放文档.

image-kgox.png

手动刷新owncloud数据命令:

[root@localhost hexo-owncloud]# docker exec -it owncloud_server entrypoint bash
su - www-data -c 'php /var/www/owncloud/occ files:scan --all'

Python代码

在/root下创建main.py

#!/usr/bin/env python3
import os
import time
import shutil
import subprocess
import fcntl
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

# 配置路径
HEXO_POSTS_DIR = "/data/owncloud/file/files/admin/files/hexo_posts"
HEXO_TARGET_DIR = "/data/hexo/source/_posts"
LOCK_FILE = "/tmp/hexo_sync.lock"

# 容器相关命令
HEXO_CONTAINER = "hexo_server"
HEXO_CLEAN_CMD = f"docker exec {HEXO_CONTAINER} hexo clean"
HEXO_GENERATE_CMD = f"docker exec {HEXO_CONTAINER} hexo generate"
HEXO_SERVER_CMD = f"docker exec -d {HEXO_CONTAINER} hexo server"

# 同步控制参数
BATCH_OPERATION_DELAY = 5  # 批量操作等待时间(秒)
MIN_SYNC_INTERVAL = 10     # 最小同步间隔(秒)

class FileSync:
    def __init__(self):
        self.last_sync_time = 0
        self.pending_sync = False
        self.batch_operation_detected = False
        self.batch_operation_time = 0
        self.lock_file = None
        self.setup_dirs()
        
    def setup_dirs(self):
        """确保所需目录存在"""
        os.makedirs(HEXO_POSTS_DIR, exist_ok=True)
        os.makedirs(HEXO_TARGET_DIR, exist_ok=True)
    
    def is_markdown_file(self, filename):
        """检查文件是否是markdown文件"""
        return filename.lower().endswith('.md') and not self.is_temp_file(filename)
    
    def is_temp_file(self, filename):
        """检查是否是临时文件"""
        return '.ocTransferId' in filename or filename.endswith('.part')
    
    def is_file_empty(self, filepath):
        """检查文件是否为空"""
        try:
            return os.path.getsize(filepath) == 0
        except:
            return True
    
    def run_command(self, cmd, background=False):
        """执行shell命令"""
        try:
            if background:
                subprocess.Popen(cmd, shell=True, start_new_session=True, 
                               stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
            else:
                subprocess.run(cmd, shell=True, check=True, 
                             stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
            return True
        except subprocess.CalledProcessError as e:
            print(f"[ERROR] 命令执行失败: {cmd}\n错误: {e}")
            return False
    
    def acquire_lock(self):
        """获取文件锁防止多进程冲突"""
        self.lock_file = open(LOCK_FILE, 'w')
        try:
            fcntl.flock(self.lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
            return True
        except IOError:
            print("[WARN] 另一个同步操作正在进行,跳过本次操作")
            return False
    
    def release_lock(self):
        """释放文件锁"""
        try:
            if self.lock_file:
                fcntl.flock(self.lock_file, fcntl.LOCK_UN)
                self.lock_file.close()
        except:
            pass
    
    def sync_hexo_posts(self, force=False):
        """同步hexo_posts到hexo目标目录"""
        current_time = time.time()
        
        # 检查同步间隔
        if not force and current_time - self.last_sync_time < MIN_SYNC_INTERVAL:
            print(f"[INFO] 同步操作过于频繁,跳过本次同步")
            return False
        
        # 获取文件锁
        if not self.acquire_lock():
            return False
        
        try:
            print("[INFO] 开始同步文件...")
            
            # 获取当前有效的markdown文件列表
            current_files = set()
            for filename in os.listdir(HEXO_POSTS_DIR):
                filepath = os.path.join(HEXO_POSTS_DIR, filename)
                if (os.path.isfile(filepath) and \
                   self.is_markdown_file(filename) and \
                   not self.is_file_empty(filepath)):
                    current_files.add(filename)
            
            # 获取目标目录现有文件列表
            target_files = set(os.listdir(HEXO_TARGET_DIR))
            
            # 需要删除的文件(在目标目录但不在源目录)
            files_to_remove = target_files - current_files
            
            # 需要添加/更新的文件
            files_to_update = current_files - target_files
            for filename in current_files & target_files:
                src_path = os.path.join(HEXO_POSTS_DIR, filename)
                dst_path = os.path.join(HEXO_TARGET_DIR, filename)
                if os.path.getmtime(src_path) > os.path.getmtime(dst_path):
                    files_to_update.add(filename)
            
            # 如果没有变化且不强制更新,则跳过
            if not force and not files_to_remove and not files_to_update:
                print("[INFO] 没有检测到文件变更,跳过同步")
                return False
            
            # 删除多余文件
            for filename in files_to_remove:
                try:
                    os.remove(os.path.join(HEXO_TARGET_DIR, filename))
                    print(f"[INFO] 删除文件: {filename}")
                except Exception as e:
                    print(f"[ERROR] 删除文件失败: {filename}\n错误: {e}")
            
            # 添加/更新文件
            for filename in files_to_update:
                src_path = os.path.join(HEXO_POSTS_DIR, filename)
                dst_path = os.path.join(HEXO_TARGET_DIR, filename)
                try:
                    shutil.copy2(src_path, dst_path)
                    os.chmod(dst_path, 0o777)
                    print(f"[INFO] 更新文件: {filename}")
                except Exception as e:
                    print(f"[ERROR] 复制文件失败: {src_path} -> {dst_path}\n错误: {e}")
            
            # 重新生成hexo
            print("[INFO] 重新生成Hexo站点...")
            self.run_command(HEXO_CLEAN_CMD)
            self.run_command(HEXO_GENERATE_CMD)
            self.run_command(HEXO_SERVER_CMD, background=True)
            
            self.last_sync_time = time.time()
            return True
            
        finally:
            self.release_lock()
    
    def check_batch_operation(self):
        """检查是否处于批量操作状态"""
        if self.batch_operation_detected:
            if time.time() - self.batch_operation_time > BATCH_OPERATION_DELAY:
                self.batch_operation_detected = False
                print("[INFO] 批量操作结束,开始同步")
                self.sync_hexo_posts()
            else:
                self.pending_sync = True
        elif self.pending_sync:
            self.pending_sync = False
            self.sync_hexo_posts()

class HexoHandler(FileSystemEventHandler):
    def __init__(self, file_sync):
        super().__init__()
        self.file_sync = file_sync
    
    def on_any_event(self, event):
        """处理所有文件系统事件"""
        if event.is_directory:
            return
            
        filename = os.path.basename(event.src_path)
        
        # 忽略临时文件和非Markdown文件
        if self.file_sync.is_temp_file(filename) or not self.file_sync.is_markdown_file(filename):
            return

        # 批量操作检测
        current_time = time.time()
        if not self.file_sync.batch_operation_detected:
            self.file_sync.batch_operation_detected = True
            self.file_sync.batch_operation_time = current_time
            print("[INFO] 检测到批量操作,等待操作完成...")
        
        # 重置批量操作计时器
        self.file_sync.batch_operation_time = current_time
        
        # 根据事件类型处理
        if event.event_type == 'deleted':
            print(f"[INFO] 检测到文件删除: {filename}")
        else:
            print(f"[INFO] 检测到文件变更: {filename} ({event.event_type})")
        
        # 计划同步
        self.file_sync.check_batch_operation()

def main():
    file_sync = FileSync()
    
    # 初始同步
    print("[INFO] 初始处理已存在的文件...")
    for filename in os.listdir(HEXO_POSTS_DIR):
        filepath = os.path.join(HEXO_POSTS_DIR, filename)
        if os.path.isfile(filepath):
            if file_sync.is_temp_file(filename):
                continue
            if file_sync.is_markdown_file(filename) and file_sync.is_file_empty(filepath):
                try:
                    os.remove(filepath)
                except:
                    pass
    
    file_sync.sync_hexo_posts(force=True)
    
    # 设置文件监控
    print("[INFO] 开始监控文件变化...")
    event_handler = HexoHandler(file_sync)
    observer = Observer()
    observer.schedule(event_handler, HEXO_POSTS_DIR, recursive=False)
    observer.start()
    
    try:
        while True:
            file_sync.check_batch_operation()
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()

if __name__ == "__main__":
    main()

安装pip并安装watchdog模块

[root@localhost ~]# dnf install python3-pip
[root@localhost ~]# python3 -m pip install watchdog

注册为系统服务

[root@localhost ~]# vi /etc/systemd/system/hexo_owncloud.service
[Unit]
Description=Hexo and OwnCloud File Sync Service
After=network.target docker.service

[Service]
Type=simple
User=root
WorkingDirectory=/root
ExecStart=/usr/bin/python3 -u /root/hexo_sync.py
Restart=always
RestartSec=5s
StandardOutput=journal
StandardError=journal
SyslogIdentifier=hexo_owncloud

[Install]
WantedBy=multi-user.target
# 文件结束
[root@localhost ~]# mv main.py hexo_sync.py
[root@localhost ~]# systemctl daemon-reload
[root@localhost ~]# systemctl start hexo_owncloud
[root@localhost ~]# systemctl enable hexo_owncloud
Created symlink /etc/systemd/system/multi-user.target.wants/hexo_owncloud.service → /etc/systemd/system/hexo_owncloud.service.

配置Hexo

主题

[root@localhost ~]# docker exec -it hexo_server bash
root@e6c4bceb9695:/hexo# git clone https://github.com/zoeingwingkei/frame.git
Cloning into 'frame'...
remote: Enumerating objects: 535, done.
remote: Counting objects: 100% (169/169), done.
remote: Compressing objects: 100% (98/98), done.
remote: Total 535 (delta 88), reused 122 (delta 71), pack-reused 366 (from 1)
Receiving objects: 100% (535/535), 2.14 MiB | 2.28 MiB/s, done.
Resolving deltas: 100% (284/284), done.
root@e6c4bceb9695:/hexo# exit
[root@localhost ~]# vi /data/hexo/_config.yml
# 将文件中以下选项进行修改
theme: fram
[root@localhost ~]# docker restart hexo_server
hexo_server
[root@localhost ~]# systemctl restart hexo_owncloud

再次访问就会发现主题已经更改完成。

更改样式

进入到主题配置文件中修改一些内容:

[root@localhost hexo]# cd /data/hexo/themes/frame/
[root@localhost frame]# vi _config.yml

修改条目:

site_brand_name: 毕业设计
profile:
  title: 基于Hexo与OwnCloud的自动化博客发布系统设计与实现
  body: 22Cloud_Computing1
  image: /szpulogo.png
  links:
    主页: /
    文章: /archives/
powered_by: # Powered by Hexo & Frame
    enable: false

进入到Hexo主配置文件中修改相关内容:

[root@localhost frame]# cd /data/hexo/
[root@localhost hexo]# vi _config.yml 

修改条目:

title: 22Cloud_Computing1
author: 22Cloud_Computing1

上传图片

使用相关软件将主页Logo图片放到该目录下:

功能演示

基础上传文章功能

将毕设实现步骤文档以markdown格式上传到owncloud中。

再刷新hexo网站页面,会发现网站内容已经更新。

查看文章内容,文章内容可以正常显示

批量上传文章功能

一次性提交12篇文章,测试是否能正常生成十二篇文章在网页中

发现新上传的12篇文章都已经正常生成网页。

批量删除文章

选择如下文件进行批量删除

回到网页发现网页中的内容已经删除完毕

更改文章内容

在owncloud上打开一篇文章,在开头添加内容并进行保存。

回到网页发现已经成功修改文章内容

版本控制

在owncloud中更改文章以后会有版本控制,点击回退版本可以回退到指定的版本。

文章的内容也会同步更改。

文章回收站

在owncloud中删除文件有回收站的功能,可以在其中对文件进行还原。