Sản phẩm — FluxFiles

Tính năng

Mọi thứ một trình quản lý file hiện đại cần

Xây dựng cho lập trình viên cần sự linh hoạt.

Đa lưu trữ

Local, AWS S3, Cloudflare R2, SFTP qua Flysystem v3 — đổi bằng config, không sửa code.

Nhúng mọi nơi

iframe + SDK postMessage. UI độc lập dùng Alpine.js (không build). Dùng được với mọi framework.

JWT auth + phân quyền

Prefix đường dẫn theo user, quyền, whitelist disk, chế độ owner-only — hoặc bắt đầu từ preset vai trò viewer/editor/admin.

Preset vai trò ACL

Cấp token từ các preset viewer/editor/admin/superadmin — chỉ một tham số là mở ra đúng các claim quyền, disk và phạm vi truy cập, khỏi phải ráp tay từng cái.

AI auto-tag

Claude / OpenAI vision — tự động alt text, tiêu đề, tags.

Cắt ảnh

Công cụ cắt ảnh với tỷ lệ khung hình sẵn.

Tối ưu ảnh

Tự tạo biến thể WebP — thumb, medium, large.

Upload presigned

Trình duyệt upload trực tiếp lên S3/R2 — không tốn bandwidth server.

Upload phân mảnh

S3 multipart cho file > 10MB.

Tìm kiếm toàn văn

Tìm kiếm có index theo tên, tiêu đề, tags — mặc định dựa trên file, hoặc dùng database SQL riêng (tùy chọn).

Metadata lưu trong database

Chế độ tùy chọn FLUXFILES_STORAGE_BACKEND=db — metadata, index tìm kiếm/thư mục và audit log được lưu trong MySQL/PostgreSQL/SQLite của riêng bạn thay vì file JSON. Dữ liệu file vẫn nằm trên storage trong cả hai trường hợp.

Thao tác hàng loạt

Chọn nhiều file để di chuyển, sao chép, xóa, tải.

Chuyển giữa các disk

Sao chép/di chuyển giữa Local ↔ S3 ↔ R2.

Thùng rác

Xóa mềm có thể khôi phục, tự dọn dẹp.

SEO metadata

Title, alt, caption, tags — metadata object S3 (cloud) hoặc sidecar JSON (local). Tìm kiếm full-text.

Chế độ tối

Sáng / tối / tự động trong UI và SDK.

16 ngôn ngữ

Mặc định tiếng Anh. Đặt locale qua SDK, URL (?locale=), hoặc FLUXFILES_LOCALE. RTL cho tiếng Ả Rập.

Nhật ký kiểm toán

Ghi log tất cả hành động ghi.

Giới hạn tốc độ

Token bucket cho mỗi người dùng.

Hạn mức lưu trữ

Giới hạn dung lượng theo JWT claims.

BYOB bucket

Người dùng gắn S3/R2 riêng — credential mã hóa trong JWT (AES-256-GCM).

Phát hiện trùng file

Khớp SHA-256 bỏ qua upload trùng trừ khi bạn force ghi đè.

Làm mới token SDK

onTokenRefresh khi 401 — gộp refresh, thử lại, và updateToken() để đổi token chủ động.

Bucket Doctor

Chẩn đoán disk S3/R2 bất kỳ — credentials, đọc/ghi/xóa, presign, CORS, multipart — kèm bản vá IAM & CORS dán là chạy. Onboard bucket BYOB trong vài phút.

Ổ đĩa SFTP

Driver thứ 3 sau S3/R2 — biến mọi VPS hay shared host thành trình quản lý tệp.

Sửa code / config

Sửa tệp văn bản và cấu hình (wp-config.php, .env, nginx.conf) ngay trên trình duyệt.

Nén & Giải nén

Tải lựa chọn dưới dạng zip, hoặc giải nén tại chỗ — chống zip-slip & zip-bomb.

Đóng dấu watermark

Thêm watermark chữ hoặc logo theo thời gian thực để bảo vệ — không đụng tệp gốc.

WebP + AVIF theo yêu cầu

Phục vụ mọi kích thước ảnh dạng AVIF/WebP cache qua 1 URL (theo Accept), kèm srcset responsive.

Phân quyền SFTP

Đổi quyền tệp Unix (chmod) trên ổ SFTP bằng hộp thoại kiểu cPanel.

SSH terminal

Nhúng ttyd / gotty / wetty của riêng bạn làm terminal trong ứng dụng trên đĩa SFTP qua claim miễn phí terminal_pty_url — FluxFiles không đi kèm shell riêng.

Deploy Git một chạm

Pull và reset repo Git trên server SFTP ngay từ trình quản lý tệp — hình dạng cố định, không phải shell tuỳ ý, hooks tắt mặc định.

Bảng dung lượng

Xem dung lượng tức thì — quota cùng phân tích theo loại và theo thư mục.

Xem trước media

Xem trực tiếp ảnh, video, audio & PDF; media local riêng tư stream an toàn (hỗ trợ Range).

Cài đặt nhanh

Chạy trong 2 phút

composer require fluxfiles/fluxfiles
cp vendor/fluxfiles/fluxfiles/.env.example vendor/fluxfiles/fluxfiles/.env
# vendor/fluxfiles/fluxfiles/.env  (read only from inside the package)
# Real environment variables win over this file and are preferred in production
# (Composer may remove a vendor .env on update).
FLUXFILES_SECRET=<output of: openssl rand -hex 32>
FLUXFILES_ALLOWED_ORIGINS=https://yourapp.com
<?php
require_once 'vendor/autoload.php';
// embed.php is not autoloaded — require it for fluxfiles_token()
require_once 'vendor/fluxfiles/fluxfiles/embed.php';

$token = fluxfiles_token([
    'user'   => 'user-123',
    'perms'  => ['read', 'write', 'delete'],
    'disks'  => ['local'],     // s3 / r2 need their own credentials in .env
    'prefix' => 'user-123/',
    'ttl'    => 3600,
]);
php vendor/bin/fluxfiles serve --port=8080
# then open http://127.0.0.1:8080/public/
composer require fluxfiles/laravel
php artisan vendor:publish --tag=fluxfiles-config
php artisan storage:link
# .env — proxy mode is the default: the API runs through your Laravel routes,
# no separate FluxFiles server is needed.
FLUXFILES_SECRET=<output of: openssl rand -hex 32>

# Only for FLUXFILES_MODE=standalone (FluxFiles on its own server):
# FLUXFILES_MODE=standalone
# FLUXFILES_ENDPOINT=https://fm.yourdomain.com
<?php
// app/FluxFiles/TenantClaims.php — REQUIRED: without a claims resolver
// <x-fluxfiles> renders "not available".
namespace App\FluxFiles;

use FluxFiles\Laravel\ClaimsResolver;
use Illuminate\Contracts\Auth\Authenticatable;

class TenantClaims implements ClaimsResolver
{
    public function resolve(Authenticatable $user): array
    {
        return [
            'prefix' => 'users/' . $user->getAuthIdentifier(),
        ];
    }
}
// config/fluxfiles.php
'claims_resolver' => \App\FluxFiles\TenantClaims::class,
// Laravel 11 / 12 — bootstrap/app.php: exclude the proxy routes from CSRF (else 419)
->withMiddleware(function (Middleware $middleware) {
    $middleware->validateCsrfTokens(except: ['api/fm/*']);
})

// Laravel 10 — app/Http/Middleware/VerifyCsrfToken.php
// protected $except = ['api/fm/*'];
<x-fluxfiles disk="local" mode="picker" height="600px" />
# Not yet on WordPress.org — install the release zip:
# 1. Download fluxfiles-<version>.zip from https://github.com/thai-pc/fluxfiles/releases
# 2. Plugins -> Add New -> Upload Plugin -> Activate
# 3. Settings -> FluxFiles: set the JWT secret (>= 32 bytes) and storage
# 4. Add the shortcode (or the FluxFiles block) to any page or post:
[fluxfiles]
npm install @fluxfiles/node   # Node >= 16
// Express / Next.js route — mint a token from your JS backend (no PHP)
import { createToken } from '@fluxfiles/node';

app.get('/fluxfiles-token', (req, res) => {
    const token = createToken({
        secret: process.env.FLUXFILES_SECRET, // >= 32 bytes
        userId: req.user.id,
        perms: ['read', 'write', 'delete'],
        prefix: `users/${req.user.id}`,
        ttl: 3600,
    });
    res.json({ token });
});
pip install fluxfiles-token
# FastAPI / Django / Flask route — mint a token from your Python backend (no PHP)
import os

from fastapi import Depends
from fluxfiles_token import create_token

@app.get("/fluxfiles-token")
def fluxfiles_token(user=Depends(current_user)):
    token = create_token(
        secret=os.environ["FLUXFILES_SECRET"],  # >= 32 bytes
        user_id=user.id,
        perms=["read", "write", "delete"],
        prefix=f"users/{user.id}",
        ttl=3600,
    )
    return {"token": token}
{
  "mcpServers": {
    "fluxfiles": {
      "command": "npx",
      "args": ["-y", "@fluxfiles/mcp"],
      "env": {
        "FLUXFILES_URL": "<your FluxFiles URL>",
        "FLUXFILES_TOKEN": "<scoped JWT>"
      }
    }
  }
}
# Claude Code (Node >= 18)
claude mcp add fluxfiles -e FLUXFILES_URL=<your FluxFiles URL> -e FLUXFILES_TOKEN=<scoped JWT> -- npx -y @fluxfiles/mcp
# Read-only by default. Opt in to writes:
FLUXFILES_MCP_WRITE=1
# and to move_to_trash:
FLUXFILES_MCP_DELETE=1

Agent chỉ chạm được vào những gì JWT của nó cho phép — disk, path prefix và quyền. Hãy cấp cho nó một token phạm vi hẹp, chỉ đọc.

npm install fluxfiles
<!-- or load a pinned CDN build (never @latest) -->
<script src="https://unpkg.com/fluxfiles@1.0.1/fluxfiles.js"></script>
<div id="file-picker" style="height:600px"></div>

<script>
FluxFiles.open({
    endpoint: 'https://your-fluxfiles-host',
    token: 'eyJhbGci...',   // JWT minted by your backend
    disk: 'local',
    mode: 'picker',
    container: '#file-picker',
    onSelect: function(file) {
        console.log('Selected:', file.url);
    },
    async onTokenRefresh() {
        const r = await fetch('/api/auth/refresh-fluxfiles-token');
        const { token } = await r.json();
        return token;
    }
});
</script>
npm install @fluxfiles/react
import { FluxFilesModal } from '@fluxfiles/react';

<FluxFilesModal
    open={open}
    endpoint="https://your-api.com"
    token={token}
    onSelect={(file) => console.log(file)}
    onClose={() => setOpen(false)}
/>
npm install @fluxfiles/vue
<script setup>
import { FluxFilesModal } from '@fluxfiles/vue';
</script>

<FluxFilesModal
    v-model:open="open"
    endpoint="https://your-api.com"
    :token="token"
    @select="onSelect"
    @close="open = false"
/>
docker run -p 8080:80 \
  -e FLUXFILES_SECRET="$(openssl rand -hex 32)" \
  -e FLUXFILES_ALLOWED_ORIGINS=https://yourapp.com \
  -v fluxfiles-data:/app/packages/core/storage \
  ghcr.io/thai-pc/fluxfiles:1.1
# UI: http://localhost:8080/public/
git clone https://github.com/thai-pc/fluxfiles.git
cd fluxfiles/packages/core
composer install
cp .env.example .env
# packages/core/.env — the placeholder secret is rejected, set a real one
FLUXFILES_SECRET=<output of: openssl rand -hex 32>
FLUXFILES_ALLOWED_ORIGINS=http://localhost:8080
# print dev tokens (run from the repo root)
php packages/core/tests/generate-token.php
php -S localhost:8080 router.php

# UI: http://localhost:8080/public/index.html?token=<token>
# ?token= is dev-only — it is stripped from the URL on boot.
# Real apps pass the JWT via the SDK / postMessage, never in a URL.

Khi lên production: đặt FLUXFILES_SECRET dài ít nhất 32 byte và FLUXFILES_ALLOWED_ORIGINS (hoặc FLUXFILES_FRAME_ANCESTORS). Dùng thư mục gốc của package làm document root và chặn /_fluxfiles/ cùng .env trong nginx. Không bao giờ đặt JWT chính vào URL — các adapter truyền nó qua postMessage. DEPLOYMENT.md

Tương thích với

Laravel WordPress React / Next.js Vue / Nuxt CKEditor 4 CKEditor 5 TinyMCE Summernote Any framework

Tích hợp

Adapter cắm-là-chạy cho stack của bạn

Các adapter chính thức nhúng FluxFiles trực tiếp vào framework và trình soạn thảo bạn đang dùng.

Dùng CKEditor hoặc TinyMCE với kho lưu trữ của riêng bạn — không cần đăng ký CKBox hay Tiny Drive. FluxFiles thêm một nút trên thanh công cụ để duyệt và chèn tệp; đây không phải sản phẩm thay thế tương đương từng tính năng.

Laravel
PHP
WordPress
Plugin
React
Component
Vue / Nuxt
Component
CKEditor 4
Plugin
CKEditor 5
Plugin
TinyMCE
Plugin
Summernote
Plugin

Nhúng

Nhúng chỉ với vài dòng

Tải fluxfiles.js, truyền JWT do server cấp, và đặt nguyên một trình quản lý file — hoặc một modal chọn file — vào bất kỳ trang nào.

<!-- npm install fluxfiles, or a pinned CDN build -->
<script src="https://unpkg.com/fluxfiles@1.0.1/fluxfiles.js"></script>

FluxFiles.open({
  endpoint: 'https://fm.example.com',
  token: 'eyJhbGci...',   // JWT from your backend
  disk: 'local',
  mode: 'picker',         // select & close
  theme: 'auto',
  onSelect(file) {
    // { url, key, name, size, disk, meta, variants }
    document.querySelector("#img").src = file.url;
  },
});
<div id="fm" style="height:600px"></div>

FluxFiles.open({
  endpoint: 'https://fm.example.com',
  token: 'eyJhbGci...',
  mode: 'browser',          // full manager, stays open
  container: '#fm',         // omit for a modal overlay
  disks: ['local', 's3', 'r2'],
});

FluxFiles.on('FM_EVENT', (e) => console.log(e.event));
FluxFiles.navigate('/photos/2024');
import { FluxFilesModal } from '@fluxfiles/react';

function Picker({ open, setOpen, token }) {
  return (
    <FluxFilesModal
      open={open}
      endpoint="https://fm.example.com"
      token={token}
      onSelect={(file) => console.log(file.url)}
      onClose={() => setOpen(false)}
    />
  );
}

Token được cấp ở phía server — đừng bao giờ đưa secret ra trình duyệt.

Tại sao FluxFiles

Xây dựng cho những gì người khác bỏ quên

Vuốt để so sánh
Tính năng FluxFiles elFinder Laravel-FM RichFilemanager Responsive FM
S3 + R2 + Local + SFTP ✓ ⚠ ⚠ ⚠ ✗
Nhúng mọi framework ✓ ⚠ ✗ ⚠ ✗
JWT auth + phân quyền ✓ ✗ ✗ ✗ ✗
SEO metadata ✓ ✗ ✗ ✗ ✗
AI auto-tag ✓ ✗ ✗ ✗ ✗
Cắt ảnh ✓ ✓ ✗ ✗ ✗
Upload phân mảnh ✓ ✗ ✗ ✗ ✗
Tìm kiếm toàn văn ✓ ✗ ✗ ✗ ✗
Chế độ tối ✓ ✗ ⚠ ✗ ✗
16 ngôn ngữ ✓ ✓ ✗ ✗ ✗
PHP ≥ 8.1 ✓ ✓ ✗ ✓ ✓
Đang maintain ✓ ✓ ⚠ ✗ ✗
UI hiện đại ✓ ✗ ⚠ ✗ ✗
Khả năng FluxFiles Filestack Uploadcare Cloudinary Supabase Storage
Self-hosted (chạy trong hạ tầng của bạn) ✓ ✗ ✗ ✗ ⚠
BYOB — bucket riêng của từng khách ✓ ⚠ ⚠ ✗ ✗
UI file manager nhúng được ✓ ✓ ✓ ⚠ ✗
Mã nguồn mở (MIT) ✓ ✗ ✗ ✗ ✓
Không phí SaaS theo storage / upload ✓ ✗ ✗ ✗ ⚠
Đa storage (S3 / R2 / local) ✓ ⚠ ✗ ✗ ✗
Scope token theo từng tenant sẵn có ✓ ⚠ ⚠ ⚠ ⚠

So sánh phản ánh gói mặc định thông thường; mỗi sản phẩm mạnh ở mảng riêng. Niche của FluxFiles là bộ ba nhúng-được, self-hosted, bring-your-own-bucket.

So sánh tính đến November 2026, không liên kết với các bên — xem repo GitHub để báo lỗi cần chỉnh sửa.

Hỏi đáp

Câu hỏi thường gặp

Xác thực hoạt động thế nào?
JWT phi trạng thái (HS256): bạn cấp token ở server với các claim chi tiết (quyền, disk, phạm vi đường dẫn, giới hạn), nên mỗi user chỉ thấy đúng phần bạn cho phép.
Người dùng có cần tài khoản FluxFiles không?
Không. FluxFiles tin tưởng JWT do chính ứng dụng của bạn cấp, nên gắn thẳng vào hệ thống auth sẵn có — không cần hệ thống user riêng.
Cần gì để chạy?
PHP 8.1+ (đã kiểm thử 8.1–8.5) với gd, curl, json, openssl, mbstring, fileinfo (thêm zip cho tính năng Extract) và Composer 2 — hoặc image Docker ghcr.io/thai-pc/fluxfiles. Trỏ tới một đĩa, đặt secret và các origin được phép rồi chạy — một VPS nhỏ là đủ.
Các người dùng khác nhau có thể có quyền khác nhau không?
Có. Bạn có thể tạo token với các claim chi tiết (đọc/ghi/xóa, disk, path prefix, giới hạn upload, đuôi file được phép, owner-only), hoặc dùng một trong bốn preset vai trò dựng sẵn — viewer, editor, admin, superadmin — để giới hạn token chỉ trong một dòng thay vì liệt kê từng claim.
Tôi có cần chạy database không?
Không. Mặc định, mọi metadata (thông tin file, chỉ mục tìm kiếm, audit log) đều nằm cạnh file của bạn dưới dạng JSON sidecar — không cần dựng hay backup gì thêm. Nếu muốn lưu phần sổ sách này trong MySQL, PostgreSQL hoặc SQLite của riêng bạn, chế độ tùy chọn FLUXFILES_STORAGE_BACKEND=db sẽ chuyển nó sang đó; dữ liệu file luôn nằm trong storage của bạn dù dùng chế độ nào.
Có giới hạn sử dụng nào không?
Bản core không đặt ra giới hạn nhân tạo nào — không giới hạn số người dùng hoạt động hàng tháng, không thúc ép nâng cấp. Bạn tự đặt giới hạn của mình qua JWT claims: hạn mức lưu trữ theo từng user, giới hạn dung lượng/đuôi file upload, và bộ giới hạn tốc độ tích hợp sẵn (mặc định 60 lượt đọc / 10 lượt ghi mỗi phút, có thể tùy chỉnh).