Compare commits

...

27 Commits

Author SHA1 Message Date
kl
171762d676 Merge pull request #740 from kekingcn/release/5.0.0
Release 5.0.0
2026-04-14 09:13:51 +08:00
kl
76e091900b Complete 5.0.0 release notes from v4.4.0 diff 2026-04-14 09:07:18 +08:00
kl
bfa4ceab90 Document 5.0.0 default config changes 2026-04-14 09:04:43 +08:00
kl
b18cfa797a Align release record page with 5.0.0 2026-04-14 09:02:29 +08:00
kl
8a117a41e8 Merge v5.0 notes into 5.0.0 release notes 2026-04-14 09:00:11 +08:00
kl
17ba41320e Prepare 5.0.0 release 2026-04-14 08:57:01 +08:00
kl
476c0bfefc Merge pull request #739 from kekingcn/paseo/ai-docs
Add AI-friendly repository guide
2026-04-14 08:13:52 +08:00
kl
1c6691d785 Clarify config defaults in AGENTS guide 2026-04-14 08:06:52 +08:00
kl
36ae290cb6 Add AI-friendly repository guide 2026-04-14 07:54:42 +08:00
kl
597715ce33 Merge pull request #738 from kekingcn/paseo/kkfileview
Refine archive preview and PDF defaults
2026-04-13 21:09:42 +08:00
kl
a8a08c1dcc Address PR review feedback 2026-04-13 21:02:59 +08:00
kl
7757729efd Refine archive preview and PDF defaults 2026-04-13 20:54:27 +08:00
kl
b246bfdac7 Merge pull request #737 from kekingcn/codex/fix-index-form-layout
fix: correct index preview form layout
2026-04-11 21:36:37 +08:00
chenkailing
d35393ba22 fix: tighten index form layout 2026-04-11 21:30:25 +08:00
chenkailing
c893dd7095 fix: keep preview parameters on one row 2026-04-11 21:12:06 +08:00
kl
9bdb18d833 Merge pull request #736 from kekingcn/codex/ci-auto-deploy-server-build
feat: add server-build auto deploy
2026-04-11 20:30:13 +08:00
chenkailing
58fc1af74f fix: address deploy review comments 2026-04-11 20:22:15 +08:00
chenkailing
1b3cf33bf0 feat: add server-build auto deploy 2026-04-11 19:36:19 +08:00
kl
c9005d0c04 Merge pull request #735 from kekingcn/codex/ci-auto-deploy-fix
fix: harden master auto deploy artifact delivery
2026-04-11 17:16:26 +08:00
chenkailing
37bda20d08 fix: harden master auto deploy artifact delivery 2026-04-11 17:08:31 +08:00
kl
1819861647 Merge pull request #734 from kekingcn/codex/ci-auto-deploy
[codex] add master auto deploy workflow
2026-04-11 16:27:37 +08:00
chenkailing
352b86b40d fix: address deploy review feedback 2026-04-11 16:14:01 +08:00
chenkailing
853ad0154f chore: drop generated pycache 2026-04-11 15:54:43 +08:00
chenkailing
c88bf04a0d feat: add master auto deploy workflow 2026-04-11 15:54:11 +08:00
kl
6a84e61ecb Merge pull request #733 from kekingcn/codex/redesign-demo-page-v1-polish
[codex] polish demo portal pages
2026-04-11 15:29:47 +08:00
chenkailing
dd6e369e6a feat: polish demo portal pages 2026-04-11 15:19:26 +08:00
chenkailing
bd20546b6d feat: redesign demo pages v1 2026-04-11 00:29:49 +08:00
34 changed files with 4193 additions and 1140 deletions

117
.github/scripts/deploy_windows_winrm.py vendored Normal file
View File

@@ -0,0 +1,117 @@
#!/usr/bin/env python3
import base64
import os
import pathlib
import sys
import uuid
import winrm
def require_env(name: str) -> str:
value = os.getenv(name, "").strip()
if not value:
raise SystemExit(f"Missing required environment variable: {name}")
return value
def optional_env(name: str, default: str) -> str:
value = os.getenv(name, "").strip()
return value if value else default
def ps_quote(value: str) -> str:
return value.replace("'", "''")
def main() -> int:
host = require_env("KK_DEPLOY_HOST")
port = optional_env("KK_DEPLOY_PORT", "5985")
username = require_env("KK_DEPLOY_USERNAME")
password = require_env("KK_DEPLOY_PASSWORD")
env_pairs = {
"KK_DEPLOY_ROOT": optional_env("KK_DEPLOY_ROOT", r"C:\kkFileView-5.0"),
"KK_DEPLOY_HEALTH_URL": optional_env("KK_DEPLOY_HEALTH_URL", "http://127.0.0.1:8012/"),
"KK_DEPLOY_REPO_URL": optional_env("KK_DEPLOY_REPO_URL", "https://github.com/kekingcn/kkFileView.git"),
"KK_DEPLOY_BRANCH": optional_env("KK_DEPLOY_BRANCH", "master"),
"KK_DEPLOY_SOURCE_ROOT": optional_env("KK_DEPLOY_SOURCE_ROOT", r"C:\kkFileView-source"),
"KK_DEPLOY_JAVA_HOME": optional_env("KK_DEPLOY_JAVA_HOME", r"C:\Program Files\jdk-21.0.2"),
"KK_DEPLOY_GIT_EXE": optional_env("KK_DEPLOY_GIT_EXE", r"C:\kkFileView-tools\git\cmd\git.exe"),
"KK_DEPLOY_MVN_CMD": optional_env("KK_DEPLOY_MVN_CMD", r"C:\kkFileView-tools\maven\bin\mvn.cmd"),
"KK_DEPLOY_MAVEN_SETTINGS": optional_env("KK_DEPLOY_MAVEN_SETTINGS", ""),
"KK_DEPLOY_DRY_RUN": optional_env("KK_DEPLOY_DRY_RUN", "false").lower(),
}
script_path = pathlib.Path(__file__).with_name("remote_windows_deploy.ps1")
script_body = script_path.read_text(encoding="utf-8")
payload = script_body.encode("utf-8-sig")
payload_b64 = base64.b64encode(payload).decode("ascii")
endpoint = f"http://{host}:{port}/wsman"
session = winrm.Session(endpoint, auth=(username, password), transport="ntlm")
suffix = uuid.uuid4().hex
remote_b64_path = fr"C:\Windows\Temp\kkfileview_deploy_{suffix}.b64"
remote_ps1_path = fr"C:\Windows\Temp\kkfileview_deploy_{suffix}.ps1"
prep = session.run_ps(
f"""
$ErrorActionPreference = 'Stop'
if (Test-Path '{ps_quote(remote_b64_path)}') {{ Remove-Item '{ps_quote(remote_b64_path)}' -Force }}
if (Test-Path '{ps_quote(remote_ps1_path)}') {{ Remove-Item '{ps_quote(remote_ps1_path)}' -Force }}
New-Item -ItemType File -Path '{ps_quote(remote_b64_path)}' -Force | Out-Null
"""
)
if prep.status_code != 0:
sys.stderr.write(prep.std_err.decode("utf-8", errors="ignore"))
return prep.status_code
chunk_size = 1200
for start in range(0, len(payload_b64), chunk_size):
chunk = payload_b64[start : start + chunk_size]
append = session.run_ps(
f"Add-Content -LiteralPath '{ps_quote(remote_b64_path)}' -Value '{chunk}'"
)
if append.status_code != 0:
sys.stderr.write(append.std_err.decode("utf-8", errors="ignore"))
return append.status_code
result = session.run_ps(
f"""
$ErrorActionPreference = 'Stop'
$raw = Get-Content -LiteralPath '{ps_quote(remote_b64_path)}' -Raw
[System.IO.File]::WriteAllBytes('{ps_quote(remote_ps1_path)}', [Convert]::FromBase64String($raw))
try {{
"""
+ "\n".join(
f" $env:{key} = '{ps_quote(value)}'" for key, value in env_pairs.items()
)
+ f"""
powershell -NoProfile -ExecutionPolicy Bypass -File '{ps_quote(remote_ps1_path)}' `
$code = $LASTEXITCODE
}} finally {{
"""
+ "\n".join(
f" Remove-Item Env:{key} -ErrorAction SilentlyContinue" for key in env_pairs
)
+ f"""
Remove-Item '{ps_quote(remote_b64_path)}' -Force -ErrorAction SilentlyContinue
Remove-Item '{ps_quote(remote_ps1_path)}' -Force -ErrorAction SilentlyContinue
}}
exit $code
"""
)
stdout = result.std_out.decode("utf-8", errors="ignore").strip()
stderr = result.std_err.decode("utf-8", errors="ignore").strip()
if stdout:
print(stdout)
if stderr:
print(stderr, file=sys.stderr)
return result.status_code
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,327 @@
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
function Write-Step {
param([string]$Message)
Write-Host "==> $Message"
}
function Get-RequiredEnv {
param([string]$Name)
$Value = [Environment]::GetEnvironmentVariable($Name)
if ([string]::IsNullOrWhiteSpace($Value)) {
throw "Missing required environment variable: $Name"
}
return $Value
}
function Get-OptionalEnv {
param(
[string]$Name,
[string]$DefaultValue
)
$Value = [Environment]::GetEnvironmentVariable($Name)
if ([string]::IsNullOrWhiteSpace($Value)) {
return $DefaultValue
}
return $Value
}
$DeployRoot = Get-OptionalEnv 'KK_DEPLOY_ROOT' 'C:\kkFileView-5.0'
$HealthUrl = Get-OptionalEnv 'KK_DEPLOY_HEALTH_URL' 'http://127.0.0.1:8012/'
$RepoUrl = Get-OptionalEnv 'KK_DEPLOY_REPO_URL' 'https://github.com/kekingcn/kkFileView.git'
$Branch = Get-OptionalEnv 'KK_DEPLOY_BRANCH' 'master'
$SourceRoot = Get-OptionalEnv 'KK_DEPLOY_SOURCE_ROOT' 'C:\kkFileView-source'
$JavaHome = Get-OptionalEnv 'KK_DEPLOY_JAVA_HOME' 'C:\Program Files\jdk-21.0.2'
$GitExe = Get-OptionalEnv 'KK_DEPLOY_GIT_EXE' 'C:\kkFileView-tools\git\cmd\git.exe'
$MvnCmd = Get-OptionalEnv 'KK_DEPLOY_MVN_CMD' 'C:\kkFileView-tools\maven\bin\mvn.cmd'
$MavenSettings = Get-OptionalEnv 'KK_DEPLOY_MAVEN_SETTINGS' ''
$DryRun = Get-OptionalEnv 'KK_DEPLOY_DRY_RUN' 'false'
$BinDir = Join-Path $DeployRoot 'bin'
$StartupScript = Join-Path $BinDir 'startup.bat'
$ReleaseDir = Join-Path $DeployRoot 'releases'
$DeployTmp = Join-Path $DeployRoot 'deploy-tmp'
$BuildOutputDir = Join-Path (Join-Path $SourceRoot 'server') 'target'
if (-not (Test-Path $DeployRoot)) {
throw "Deploy root not found: $DeployRoot"
}
if (-not (Test-Path $BinDir)) {
throw "Bin directory not found: $BinDir"
}
if (-not (Test-Path $StartupScript)) {
throw "Startup script not found: $StartupScript"
}
$CurrentJar = Get-ChildItem $BinDir -Filter 'kkFileView-*.jar' | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if (-not $CurrentJar) {
throw "No kkFileView jar found in $BinDir"
}
$JavaExe = Join-Path $JavaHome 'bin\java.exe'
if (-not (Test-Path $JavaExe)) {
throw "JDK 21 java executable not found: $JavaExe"
}
if (-not (Test-Path $GitExe)) {
throw "Git executable not found: $GitExe"
}
if (-not (Test-Path $MvnCmd)) {
throw "Maven executable not found: $MvnCmd"
}
if (-not [string]::IsNullOrWhiteSpace($MavenSettings) -and -not (Test-Path $MavenSettings)) {
throw "Maven settings file not found: $MavenSettings"
}
$JarName = $CurrentJar.Name
$JarPath = $CurrentJar.FullName
Write-Step "Deploy root: $DeployRoot"
Write-Step "Current jar: $JarPath"
Write-Step "Startup script: $StartupScript"
Write-Step "Health url: $HealthUrl"
Write-Step "Source root: $SourceRoot"
Write-Step "Branch: $Branch"
Write-Step "Git exe: $GitExe"
Write-Step "Maven cmd: $MvnCmd"
Write-Step "Java home: $JavaHome"
if (-not [string]::IsNullOrWhiteSpace($MavenSettings)) {
Write-Step "Maven settings: $MavenSettings"
}
function Invoke-External {
param(
[string]$FilePath,
[string[]]$Arguments,
[string]$WorkingDirectory = $null
)
$previous = $null
if ($WorkingDirectory) {
$previous = Get-Location
Set-Location $WorkingDirectory
}
try {
& $FilePath @Arguments
if ($LASTEXITCODE -ne 0) {
throw "Command failed ($LASTEXITCODE): $FilePath $($Arguments -join ' ')"
}
} finally {
if ($previous) {
Set-Location $previous
}
}
}
function Assert-SafeSourceRoot {
param([string]$PathToCheck)
$FullPath = [System.IO.Path]::GetFullPath($PathToCheck)
$RootPath = [System.IO.Path]::GetPathRoot($FullPath)
if ($FullPath.TrimEnd('\') -eq $RootPath.TrimEnd('\')) {
throw "Refusing to use drive root as source root: $FullPath"
}
$DangerousLeafNames = @(
'Windows',
'Users',
'Program Files',
'Program Files (x86)',
'ProgramData'
)
$LeafName = Split-Path -Leaf $FullPath.TrimEnd('\')
if ($DangerousLeafNames -contains $LeafName) {
throw "Refusing to use a high-risk source root path: $FullPath"
}
}
$env:JAVA_HOME = $JavaHome
$env:Path = (Join-Path $JavaHome 'bin') + ';' + (Split-Path -Parent $GitExe) + ';' + (Split-Path -Parent $MvnCmd) + ';' + $env:Path
Write-Step 'Validating Git executable'
Invoke-External -FilePath $GitExe -Arguments @('--version')
Write-Step 'Validating Maven executable'
$MavenVersionArgs = @('-version')
if (-not [string]::IsNullOrWhiteSpace($MavenSettings)) {
$MavenVersionArgs = @('-s', $MavenSettings, '-version')
}
Invoke-External -FilePath $MvnCmd -Arguments $MavenVersionArgs
if ($DryRun -eq 'true') {
Write-Step "Dry run enabled, remote validation finished"
return
}
New-Item -ItemType Directory -Force -Path $ReleaseDir | Out-Null
New-Item -ItemType Directory -Force -Path $DeployTmp | Out-Null
function Sync-Repository {
Assert-SafeSourceRoot -PathToCheck $SourceRoot
if (-not (Test-Path (Join-Path $SourceRoot '.git'))) {
if (Test-Path $SourceRoot) {
Remove-Item $SourceRoot -Recurse -Force
}
$parent = Split-Path -Parent $SourceRoot
if ($parent) {
New-Item -ItemType Directory -Force -Path $parent | Out-Null
}
Write-Step "Cloning repository from $RepoUrl"
Invoke-External -FilePath $GitExe -Arguments @('clone', '--depth', '1', '--branch', $Branch, '--single-branch', $RepoUrl, $SourceRoot)
return
}
Write-Step "Fetching latest branch state from origin/$Branch"
Invoke-External -FilePath $GitExe -Arguments @('remote', 'set-url', 'origin', $RepoUrl) -WorkingDirectory $SourceRoot
Invoke-External -FilePath $GitExe -Arguments @('fetch', '--prune', '--depth', '1', 'origin', $Branch) -WorkingDirectory $SourceRoot
Invoke-External -FilePath $GitExe -Arguments @('checkout', '-B', $Branch, "origin/$Branch") -WorkingDirectory $SourceRoot
Invoke-External -FilePath $GitExe -Arguments @('reset', '--hard', "origin/$Branch") -WorkingDirectory $SourceRoot
Invoke-External -FilePath $GitExe -Arguments @('clean', '-fd') -WorkingDirectory $SourceRoot
}
function Build-KkFileView {
Write-Step 'Building kkFileView from source'
$BuildArgs = @('-B', 'clean', 'package', '-Dmaven.test.skip=true', '--file', 'pom.xml')
if (-not [string]::IsNullOrWhiteSpace($MavenSettings)) {
$BuildArgs = @('-s', $MavenSettings) + $BuildArgs
}
Invoke-External -FilePath $MvnCmd -Arguments $BuildArgs -WorkingDirectory $SourceRoot
}
Sync-Repository
Build-KkFileView
$DownloadedJars = Get-ChildItem $BuildOutputDir -Filter 'kkFileView-*.jar' -File
if (-not $DownloadedJars) {
throw "No kkFileView jar found in build output: $BuildOutputDir"
}
if ($DownloadedJars.Count -ne 1) {
throw "Expected exactly one kkFileView jar in build output, found $($DownloadedJars.Count)"
}
$DownloadedJar = $DownloadedJars[0]
$Timestamp = Get-Date -Format 'yyyyMMddHHmmss'
$BackupJar = Join-Path $ReleaseDir ("{0}.{1}.bak" -f $JarName, $Timestamp)
function Stop-KkFileView {
foreach ($Process in @(Get-KkFileViewJavaProcesses) + @(Get-KkFileViewLauncherProcesses)) {
Write-Step "Stopping process $($Process.ProcessId)"
Stop-Process -Id $Process.ProcessId -Force -ErrorAction SilentlyContinue
}
}
function Get-KkFileViewJavaProcesses {
$JarPattern = [regex]::Escape($JarName)
return Get-CimInstance Win32_Process | Where-Object {
$_.Name -match '^java(\.exe)?$' -and $_.CommandLine -and $_.CommandLine -match $JarPattern
}
}
function Get-KkFileViewLauncherProcesses {
$StartupPattern = [regex]::Escape([System.IO.Path]::GetFileName($StartupScript))
return Get-CimInstance Win32_Process | Where-Object {
$_.Name -ieq 'cmd.exe' -and $_.CommandLine -and $_.CommandLine -match $StartupPattern
}
}
function Wait-KkFileViewStopped {
param([int]$TimeoutSeconds = 30)
for ($i = 0; $i -lt $TimeoutSeconds; $i++) {
$JavaProcesses = @(Get-KkFileViewJavaProcesses)
$CmdProcesses = @(Get-KkFileViewLauncherProcesses)
if ((@($JavaProcesses).Count + @($CmdProcesses).Count) -eq 0) {
return $true
}
Start-Sleep -Seconds 1
}
return $false
}
function Start-KkFileView {
Write-Step "Starting kkFileView"
$CreateResult = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{
CommandLine = ('cmd.exe /c ""' + $StartupScript + '""')
CurrentDirectory = $BinDir
}
if ($CreateResult.ReturnValue -ne 0) {
throw "Failed to start kkFileView launcher, Win32_Process.Create returned $($CreateResult.ReturnValue)"
}
Write-Step "Launcher process created with pid $($CreateResult.ProcessId)"
}
function Wait-Health {
param([string]$Url)
$SuccessfulChecks = 0
for ($i = 0; $i -lt 24; $i++) {
Start-Sleep -Seconds 5
try {
$Response = Invoke-WebRequest -Uri $Url -UseBasicParsing -TimeoutSec 5
if ($Response.StatusCode -eq 200 -and @(Get-KkFileViewJavaProcesses).Count -gt 0) {
$SuccessfulChecks++
} else {
$SuccessfulChecks = 0
}
if ($SuccessfulChecks -ge 3) {
return $true
}
} catch {
$SuccessfulChecks = 0
Start-Sleep -Milliseconds 200
}
}
return $false
}
Write-Step "Backing up current jar to $BackupJar"
Copy-Item $JarPath $BackupJar -Force
Stop-KkFileView
if (-not (Wait-KkFileViewStopped)) {
throw "Timed out waiting for the previous kkFileView process to exit"
}
Write-Step "Replacing jar with artifact output"
Copy-Item $DownloadedJar.FullName $JarPath -Force
Start-KkFileView
if (-not (Wait-Health -Url $HealthUrl)) {
Write-Step "Health check failed, rolling back"
Stop-KkFileView
if (-not (Wait-KkFileViewStopped)) {
throw "Timed out waiting for the failed kkFileView process to exit during rollback"
}
Copy-Item $BackupJar $JarPath -Force
Start-KkFileView
if (-not (Wait-Health -Url $HealthUrl)) {
throw "Deployment failed and rollback health check also failed"
}
throw "Deployment failed, rollback completed successfully"
}
Write-Step "Deployment completed successfully"

View File

@@ -0,0 +1,52 @@
name: Master Auto Deploy
on:
push:
branches: [ master ]
workflow_dispatch:
concurrency:
group: master-auto-deploy-production
cancel-in-progress: false
permissions:
contents: read
jobs:
deploy-windows:
runs-on: ubuntu-22.04
env:
KK_DEPLOY_HOST: ${{ secrets.KK_DEPLOY_HOST }}
KK_DEPLOY_PORT: ${{ secrets.KK_DEPLOY_PORT }}
KK_DEPLOY_USERNAME: ${{ secrets.KK_DEPLOY_USERNAME }}
KK_DEPLOY_PASSWORD: ${{ secrets.KK_DEPLOY_PASSWORD }}
KK_DEPLOY_ROOT: ${{ secrets.KK_DEPLOY_ROOT }}
KK_DEPLOY_HEALTH_URL: ${{ secrets.KK_DEPLOY_HEALTH_URL }}
KK_DEPLOY_REPO_URL: ${{ vars.KK_DEPLOY_REPO_URL }}
KK_DEPLOY_BRANCH: ${{ vars.KK_DEPLOY_BRANCH }}
KK_DEPLOY_SOURCE_ROOT: ${{ vars.KK_DEPLOY_SOURCE_ROOT }}
KK_DEPLOY_JAVA_HOME: ${{ vars.KK_DEPLOY_JAVA_HOME }}
KK_DEPLOY_GIT_EXE: ${{ vars.KK_DEPLOY_GIT_EXE }}
KK_DEPLOY_MVN_CMD: ${{ vars.KK_DEPLOY_MVN_CMD }}
KK_DEPLOY_MAVEN_SETTINGS: ${{ vars.KK_DEPLOY_MAVEN_SETTINGS }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install WinRM dependencies
run: pip install pywinrm
- name: Validate deploy secrets
run: |
test -n "$KK_DEPLOY_HOST" || (echo "Missing secret: KK_DEPLOY_HOST" && exit 1)
test -n "$KK_DEPLOY_USERNAME" || (echo "Missing secret: KK_DEPLOY_USERNAME" && exit 1)
test -n "$KK_DEPLOY_PASSWORD" || (echo "Missing secret: KK_DEPLOY_PASSWORD" && exit 1)
- name: Deploy to Windows server
run: python .github/scripts/deploy_windows_winrm.py

1
.gitignore vendored
View File

@@ -26,6 +26,7 @@ nbdist/
### VS Code ### ### VS Code ###
.vscode/ .vscode/
.DS_Store .DS_Store
.artifacts/
server/src/main/cache/ server/src/main/cache/
server/src/main/file/ server/src/main/file/

230
AGENTS.md Normal file
View File

@@ -0,0 +1,230 @@
# AGENTS.md
This document is for coding agents and automation tools working in this repository.
## Project Overview
- Project: `kkFileView`
- Stack: Spring Boot + Freemarker + Redis/Redisson (optional) + JODConverter + front-end preview pages
- Main module: `server`
- Default local URL: `http://127.0.0.1:8012/`
- Production demo: `https://file.kkview.cn/`
This repository is a document preview service. Most user-facing work falls into one of these areas:
1. preview routing and file-type dispatch
2. conversion pipelines for Office / PDF / CAD / archives / images
3. Freemarker preview templates under `server/src/main/resources/web`
4. CI, E2E fixtures, and production deployment automation
## Repository Layout
- `server/`
Main application code, templates, config, packaged artifacts
- `server/src/main/java/cn/keking/`
Core Java application code
- `server/src/main/resources/web/`
Freemarker preview templates
- `server/src/main/resources/static/`
Front-end static assets used by preview pages
- `server/src/main/config/`
Main runtime config files
- `server/src/main/bin/`
Local startup/dev scripts
- `tests/e2e/`
Playwright-based end-to-end tests and fixtures
- `.github/workflows/`
CI and deployment workflows
- `.github/scripts/`
Windows production deployment scripts over WinRM
## Key Entry Points
- App entry:
- `server/src/main/java/cn/keking/ServerMain.java`
- Preview controller:
- `server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java`
- File attribute parsing / request handling:
- `server/src/main/java/cn/keking/service/FileHandlerService.java`
- Office preview flow:
- `server/src/main/java/cn/keking/service/impl/OfficeFilePreviewImpl.java`
- PDF preview flow:
- `server/src/main/java/cn/keking/service/impl/PdfFilePreviewImpl.java`
- Archive extraction:
- `server/src/main/java/cn/keking/service/CompressFileReader.java`
## Important Templates
- `server/src/main/resources/web/compress.ftl`
Archive directory/tree preview page
- `server/src/main/resources/web/pdf.ftl`
PDF preview container page
- `server/src/main/resources/web/picture.ftl`
Single image preview page
- `server/src/main/resources/web/officePicture.ftl`
Office/PDF image-mode preview page
- `server/src/main/resources/web/officeweb.ftl`
Front-end xlsx/html preview page
When debugging UX issues, inspect the exact template selected by the preview flow first. Do not assume two similar preview pages share the same CSS or behavior.
## Local Development
### Recommended dev mode
Use:
```bash
./server/src/main/bin/dev.sh
```
This runs Spring Boot with resource hot reload using:
- `spring-boot:run`
- `-Dspring-boot.run.addResources=true`
- `server/src/main/config/application.properties`
For front-end template or CSS/JS edits, prefer `dev.sh` over rebuilding jars repeatedly.
### Jar build
```bash
mvn -q -pl server -DskipTests package
```
### Main test command used in CI
```bash
mvn -B package -Dmaven.test.skip=true --file pom.xml
```
## Configuration Notes
Primary runtime config used by the scripts and defaults committed in this repository:
- `server/src/main/config/application.properties`
Optional environment-specific config:
- `server/src/main/config/test.properties`
Be careful: the repository defaults point at `application.properties`. If a deployment environment explicitly starts the app with `test.properties`, treat that as an environment-specific override rather than the repository default. Always verify the actual startup command before assuming which config file is active.
Examples of config that commonly affects behavior:
- `office.preview.type`
- `office.preview.switch.disabled`
- `trust.host`
- `not.trust.host`
- `file.upload.disable`
## Preview Behavior Notes
- Office files can render in `pdf` mode or `image` mode.
- PDF preview uses `pdf.ftl`.
- Single images use `picture.ftl`.
- Office image-mode previews use `officePicture.ftl`.
- Archive previews are not simple file lists; they can load nested previews via the archive UI in `compress.ftl`.
When changing preview defaults, verify both:
1. server-side default config
2. front-end mode-switch links/buttons
## Archive Preview Notes
Archive preview is a sensitive area because it combines:
- directory tree generation
- extraction to disk
- nested preview URL construction
- inline iframe loading
If an archive-contained Office file gets stuck on loading:
1. verify the extracted file on disk is not corrupted
2. verify conversion output exists
3. verify the preview template points to the correct generated artifact
4. verify the running Office manager / LibreOffice process is healthy
Do not assume loading forever is a front-end issue.
## Testing
### Targeted Java tests
Example targeted test:
```bash
mvn -q -pl server -Dtest=PdfViewerCompatibilityTests test
```
### E2E tests
See:
- `tests/e2e/README.md`
PR E2E currently covers:
- common preview smoke tests
- Office smoke tests
- archive smoke tests
- basic security and performance checks
## CI / Deployment
### CI
- `maven.yml`
- builds on `push` to `master`
- builds on PRs targeting `master`
- `pr-e2e-mvp.yml`
- runs E2E on PRs to `master`
### Production deployment
- `master-auto-deploy.yml`
- triggers on push to `master`
- deploys to Windows over WinRM
Deployment script:
- `.github/scripts/remote_windows_deploy.ps1`
Important operational detail:
- the committed `bin/startup.bat` in this repo points at `..\config\application.properties`
- if production uses a different config file, treat that as an out-of-repo server override rather than a repository default
If a production config change does not take effect, inspect the actual startup command or deployed `startup.bat` on the server first and verify which config file path it is using.
## Working Conventions For Agents
- Prefer minimal, targeted changes over wide refactors.
- Inspect the active preview template before editing CSS.
- Verify whether behavior is controlled by config, back-end routing, or front-end template logic before changing code.
- For production/debug tasks, distinguish clearly between:
- repository source defaults
- deployed server config
- runtime process arguments
- When changing defaults, mention whether the change affects:
- local dev only
- repository default config
- deployed server config
- existing query-param overrides
## Suggested Validation Checklist
For preview-related changes, validate as many of these as apply:
1. target URL returns `200`
2. selected template is the expected one
3. generated intermediate artifacts exist when required
4. target UI element or style change is actually present in rendered HTML
5. targeted Java test passes
6. relevant E2E path is still compatible
## Non-Goals
This file is not a replacement for user-facing product documentation. Keep it focused on helping coding agents navigate the codebase and make correct changes faster.

View File

@@ -1,4 +1,4 @@
FROM keking/kkfileview-base:4.4.0 FROM keking/kkfileview-base:5.0.0
ADD server/target/kkFileView-*.tar.gz /opt/ ADD server/target/kkFileView-*.tar.gz /opt/
ENV KKFILEVIEW_BIN_FOLDER=/opt/kkFileView-4.4.0/bin ENV KKFILEVIEW_BIN_FOLDER=/opt/kkFileView-5.0.0/bin
ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dspring.config.location=/opt/kkFileView-4.4.0/config/application.properties","-jar","/opt/kkFileView-4.4.0/bin/kkFileView-4.4.0.jar"] ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dspring.config.location=/opt/kkFileView-5.0.0/config/application.properties","-jar","/opt/kkFileView-5.0.0/bin/kkFileView-5.0.0.jar"]

View File

@@ -149,7 +149,7 @@ pdf预览模式预览效果如下
### 历史更新记录 ### 历史更新记录
#### > 2026年01月20v5.0 版本发布 #### > 2026年04月14v5.0.0 版本发布
#### 优化内容 #### 优化内容
1. xlsx 前端解析优化 - 提升Excel文件前端渲染性能 1. xlsx 前端解析优化 - 提升Excel文件前端渲染性能
2. 图片解析优化 - 改进图片处理机制 2. 图片解析优化 - 改进图片处理机制
@@ -159,6 +159,10 @@ pdf预览模式预览效果如下
6. ftp多客户端接入优化 - 提升FTP服务兼容性 6. ftp多客户端接入优化 - 提升FTP服务兼容性
7. 首页目录访问优化 - 采用post服务端分页机制 7. 首页目录访问优化 - 采用post服务端分页机制
8. marked 解析优化 - 改进Markdown渲染 8. marked 解析优化 - 改进Markdown渲染
9. 压缩包预览页重构为单工作区布局支持目录折叠与右侧内嵌预览
10. 优化压缩包内文件类型标识以及单图预览页的展示样式
11. 补充面向工程自动化与编码代理的仓库说明文档
12. 重构演示门户页面包括首页接入说明版本记录与赞助页
#### 新增功能 #### 新增功能
1. msg邮件解析 - 新增msg格式邮件文件预览支持 1. msg邮件解析 - 新增msg格式邮件文件预览支持
@@ -179,6 +183,12 @@ pdf预览模式预览效果如下
2. 安全问题 - 修复安全漏洞 2. 安全问题 - 修复安全漏洞
3. 图片水印不全问题 - 修复水印显示不完整 3. 图片水印不全问题 - 修复水印显示不完整
4. SSL自签证书接入问题 - 修复自签名证书兼容性 4. SSL自签证书接入问题 - 修复自签名证书兼容性
5. 修复压缩包内 Office 文件在重复解压后被追加写坏导致一直卡在加载中的问题
6. Office 默认预览改为 PDF 模式 PDF 预览默认打开缩略图侧栏
7. 启动脚本改为自动发现当前发布包中的 jar移除过时的硬编码 jar 名称
8. 更新 Docker 与发布辅助文档使其与 5.0.0 发布线保持一致
9. 修复 OFD 表格竖线溢出导致的渲染异常
10. 修复 PDF.js 兼容性补丁避免兼容环境下的预览报错
#### 更新内容 #### 更新内容
1. JDK版本要求 - 强制要求JDK 21及以上版本 1. JDK版本要求 - 强制要求JDK 21及以上版本
@@ -189,6 +199,8 @@ pdf预览模式预览效果如下
6. tif后端异步转换优化 - 实现多线程异步转换 6. tif后端异步转换优化 - 实现多线程异步转换
7. 视频后端异步转换优化 - 实现多线程异步转换 7. 视频后端异步转换优化 - 实现多线程异步转换
8. CAD后端异步转换优化 - 实现多线程异步转换 8. CAD后端异步转换优化 - 实现多线程异步转换
9. 默认预览配置策略调整 - Office 预览默认切换为 PDF 模式默认隐藏图片/PDF 模式切换按钮 PDF 预览默认展开缩略图侧栏若升级后仍需保持旧的图片优先体验请显式设置 `office.preview.type=image` `office.preview.switch.disabled=false`
10. 信任域名配置匹配策略扩展 - `trust.host` 及相关规则现已支持通配符和 CIDR 匹配升级后如果你依赖域名/IP 模式匹配需要重新检查白名单和黑名单的实际生效范围
#### > 2025年01月16日v4.4.0 版本发布 #### > 2025年01月16日v4.4.0 版本发布
@@ -468,4 +480,3 @@ dcm医疗数位影像 引用于 [dcmjs](https://github.com/dcmjs-org/dcmjs )开
- 本项目诞生于[凯京集团]在取得公司高层同意后以 Apache 协议开源出来反哺社区在此特别感谢凯京集团以及集团领导[@唐老大](https://github.com/tangshd)的支持、@端木详笑的贡献。 - 本项目诞生于[凯京集团]在取得公司高层同意后以 Apache 协议开源出来反哺社区在此特别感谢凯京集团以及集团领导[@唐老大](https://github.com/tangshd)的支持、@端木详笑的贡献。
- 本项目已脱离公司由[KK开源社区]维护发展壮大感谢所有给 kkFileView Issue Pr 开发者 - 本项目已脱离公司由[KK开源社区]维护发展壮大感谢所有给 kkFileView Issue Pr 开发者
- 本项目引入的第三方组件已在 '关于引用' 列表列出感谢这些项目 kkFileView 更出色 - 本项目引入的第三方组件已在 '关于引用' 列表列出感谢这些项目 kkFileView 更出色

View File

@@ -65,9 +65,9 @@ URL[https://file.kkview.cn](https://file.kkview.cn)
## Change History ## Change History
### Version 5.0 (January 20, 2026) ### Version 5.0.0 (April 14, 2026)
#### Optimizations #### Improvements
1. Enhanced xlsx front-end parsing - Improved Excel file front-end rendering performance 1. Enhanced xlsx front-end parsing - Improved Excel file front-end rendering performance
2. Optimized image parsing - Enhanced image processing mechanism 2. Optimized image parsing - Enhanced image processing mechanism
3. Improved tif parsing - Enhanced TIF format support 3. Improved tif parsing - Enhanced TIF format support
@@ -76,6 +76,10 @@ URL[https://file.kkview.cn](https://file.kkview.cn)
6. Optimized ftp multi-client access - Improved FTP service compatibility 6. Optimized ftp multi-client access - Improved FTP service compatibility
7. Enhanced home page directory access - Implemented post server-side pagination mechanism 7. Enhanced home page directory access - Implemented post server-side pagination mechanism
8. Improved marked parsing - Enhanced Markdown rendering 8. Improved marked parsing - Enhanced Markdown rendering
9. Redesigned archive preview into a single workspace with a collapsible tree and inline file preview
10. Improved archive preview file-type badges and single-image preview styling
11. Added an agent-focused repository guide for engineering automation and maintenance
12. Refreshed the demo portal pages, including the index, integration guide, release record, and sponsor pages
#### New Features #### New Features
1. msg email parsing - Added support for msg format email file preview 1. msg email parsing - Added support for msg format email file preview
@@ -96,6 +100,12 @@ URL[https://file.kkview.cn](https://file.kkview.cn)
2. Security issues - Fixed security vulnerabilities 2. Security issues - Fixed security vulnerabilities
3. Incomplete image watermark issues - Fixed incomplete watermark display 3. Incomplete image watermark issues - Fixed incomplete watermark display
4. SSL self-signed certificate access issues - Fixed compatibility with self-signed certificates 4. SSL self-signed certificate access issues - Fixed compatibility with self-signed certificates
5. Fixed archive-contained Office files that could stay stuck on loading because repeated extraction appended to existing files
6. Default Office preview now prefers PDF mode, and PDF preview opens with the thumbnail sidebar visible by default
7. Updated startup scripts to discover the packaged jar dynamically instead of relying on stale hard-coded jar names
8. Updated Docker and release helper docs to align with the 5.0.0 release line
9. Fixed OFD table border overflow rendering issues
10. Refined the PDF.js compatibility polyfill to avoid preview errors in compatibility environments
#### Updates #### Updates
1. JDK version requirement - Mandatory requirement for JDK 21 or higher 1. JDK version requirement - Mandatory requirement for JDK 21 or higher
@@ -106,6 +116,8 @@ URL[https://file.kkview.cn](https://file.kkview.cn)
6. tif backend async conversion optimization - Implemented multi-threaded asynchronous conversion 6. tif backend async conversion optimization - Implemented multi-threaded asynchronous conversion
7. Video backend async conversion optimization - Implemented multi-threaded asynchronous conversion 7. Video backend async conversion optimization - Implemented multi-threaded asynchronous conversion
8. CAD backend async conversion optimization - Implemented multi-threaded asynchronous conversion 8. CAD backend async conversion optimization - Implemented multi-threaded asynchronous conversion
9. Default preview configuration strategy adjusted - Office preview now defaults to PDF mode, the mode switch is hidden by default, and PDF preview opens with the thumbnail sidebar visible. If you need the previous image-first behavior after upgrade, explicitly set `office.preview.type=image` and `office.preview.switch.disabled=false`.
10. Trust host configuration matching expanded - `trust.host` and related rules now support wildcard and CIDR matching, which may broaden or narrow effective allow/deny behavior after upgrade depending on your patterns
### Version 4.4.0 (January 16, 2025) ### Version 4.4.0 (January 16, 2025)

57
doc/ci-auto-deploy.md Normal file
View File

@@ -0,0 +1,57 @@
# kkFileView master 自动部署
当前线上 Windows 服务器的实际部署信息如下
- 部署根目录`C:\kkFileView-5.0`
- 运行 jar`C:\kkFileView-5.0\bin\kkFileView-<当前项目版本>.jar`
- 启动脚本`C:\kkFileView-5.0\bin\startup.bat`
- 运行配置`C:\kkFileView-5.0\config\test.properties`
- 健康检查地址`http://127.0.0.1:8012/`
当前自动部署链路采用服务器拉最新源码并本机编译的方式
1. 通过 WinRM 连接 Windows 服务器
2. 在服务器上的源码目录执行 `git fetch/reset/clean`同步到 `origin/$KK_DEPLOY_BRANCH`默认 `master`
3. 使用服务器上的 JDK 21 Maven 执行 `mvn clean package -Dmaven.test.skip=true`
4. 备份线上 jar替换为新构建产物
5. 使用现有 `startup.bat` 重启并做健康检查
6. 如果健康检查失败则自动回滚旧 jar 并重新拉起
## 需要配置的 GitHub Secrets
- `KK_DEPLOY_HOST`
- `KK_DEPLOY_USERNAME`
- `KK_DEPLOY_PASSWORD`
以下部署参数当前由 workflow GitHub Secrets 读取如果未单独配置则使用脚本默认值
- `KK_DEPLOY_PORT=5985`
- `KK_DEPLOY_ROOT=C:\kkFileView-5.0`
- `KK_DEPLOY_HEALTH_URL=http://127.0.0.1:8012/`
下面这些非敏感参数可以通过 workflow env GitHub Variables 覆盖未配置时会使用默认值
- `KK_DEPLOY_REPO_URL=https://github.com/kekingcn/kkFileView.git`
- `KK_DEPLOY_BRANCH=master`
- `KK_DEPLOY_SOURCE_ROOT=C:\kkFileView-source`
- `KK_DEPLOY_JAVA_HOME=C:\Program Files\jdk-21.0.2`
- `KK_DEPLOY_GIT_EXE=C:\kkFileView-tools\git\cmd\git.exe`
- `KK_DEPLOY_MVN_CMD=C:\kkFileView-tools\maven\bin\mvn.cmd`
- `KK_DEPLOY_MAVEN_SETTINGS=`
如果服务器到 GitHub 的拉取速度不稳定也可以把 `KK_DEPLOY_REPO_URL` 改成你自己的 Git 镜像地址
如果服务器访问 Maven Central 不稳定也可以通过 `KK_DEPLOY_MAVEN_SETTINGS` 指向自定义 `settings.xml`切换到就近镜像仓库
## 服务器前置环境
服务器需要具备以下工具
- Git for Windows推荐安装在 `C:\kkFileView-tools\git`
- Apache Maven 3.9.x推荐安装在 `C:\kkFileView-tools\maven`
- JDK 21当前线上已存在`C:\Program Files\jdk-21.0.2`
## Workflow
新增 workflow`.github/workflows/master-auto-deploy.yml`
- 触发条件`push` `master`或手动 `workflow_dispatch`
- 部署方式WinRM + 服务器源码同步 + 服务器本机 Maven 编译 + jar 替换/回滚

View File

@@ -7,10 +7,10 @@
然后使用 kkfileview-base 作为基础镜像进行构建加快 kkfileview docker 镜像构建与发布 然后使用 kkfileview-base 作为基础镜像进行构建加快 kkfileview docker 镜像构建与发布
执行如下命令即可构建基础镜像 执行如下命令即可构建基础镜像
> 这里镜像 tag 4.4.0 为例本项目所维护的 Dockerfile 文件考虑了跨平台兼容性 如果你需要用到 arm64 架构镜像, 则在arm64 架构机器上同样执行下面的构建命令即可 > 这里镜像 tag 5.0.0 为例本项目所维护的 Dockerfile 文件考虑了跨平台兼容性 如果你需要用到 arm64 架构镜像, 则在arm64 架构机器上同样执行下面的构建命令即可
```shell ```shell
docker build --tag keking/kkfileview-base:4.4.0 . docker build --tag keking/kkfileview-base:5.0.0 .
``` ```
@@ -46,5 +46,5 @@ docker build --tag keking/kkfileview-base:4.4.0 .
现在就可以愉快地开始构建了构建命令示例: 现在就可以愉快地开始构建了构建命令示例:
```shell ```shell
docker buildx build --platform=linux/amd64,linux/arm64 -t keking/kkfileview-base:4.4.0 --push . docker buildx build --platform=linux/amd64,linux/arm64 -t keking/kkfileview-base:5.0.0 --push .
``` ```

View File

@@ -8,10 +8,10 @@ Then, use kkfileview-base as the base image to build and speed up the kkfileview
To build the base image, run the following command: To build the base image, run the following command:
> In this example, the image tag is 4.4.0. The Dockerfile maintained in this project considers cross-platform compatibility. If you need an arm64 architecture image, run the same build command on an arm64 architecture machine. > In this example, the image tag is 5.0.0. The Dockerfile maintained in this project considers cross-platform compatibility. If you need an arm64 architecture image, run the same build command on an arm64 architecture machine.
```shell ```shell
docker build --tag keking/kkfileview-base:4.4.0 . docker build --tag keking/kkfileview-base:5.0.0 .
``` ```
@@ -49,5 +49,5 @@ Assuming the current machine is amd64 (x86_64) architecture, you'll need to enab
Now you can enjoy the building. Heres an example build command: Now you can enjoy the building. Heres an example build command:
```shell ```shell
docker buildx build --platform=linux/amd64,linux/arm64 -t keking/kkfileview-base:4.4.0 --push . docker buildx build --platform=linux/amd64,linux/arm64 -t keking/kkfileview-base:5.0.0 --push .
``` ```

View File

@@ -6,7 +6,7 @@
<groupId>cn.keking</groupId> <groupId>cn.keking</groupId>
<artifactId>kkFileView-parent</artifactId> <artifactId>kkFileView-parent</artifactId>
<version>5.0</version> <version>5.0.0</version>
<properties> <properties>
<!-- ========== Java 和编译配置 ========== --> <!-- ========== Java 和编译配置 ========== -->
@@ -110,4 +110,4 @@
<system>github</system> <system>github</system>
<url>https://github.com/kekingcn/kkFileView/issues</url> <url>https://github.com/kekingcn/kkFileView/issues</url>
</issueManagement> </issueManagement>
</project> </project>

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<artifactId>kkFileView-parent</artifactId> <artifactId>kkFileView-parent</artifactId>
<groupId>cn.keking</groupId> <groupId>cn.keking</groupId>
<version>5.0</version> <version>5.0.0</version>
</parent> </parent>
<artifactId>kkFileView</artifactId> <artifactId>kkFileView</artifactId>
@@ -52,6 +52,12 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId> <artifactId>spring-boot-starter-actuator</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- ========== 文档格式转换 ========== --> <!-- ========== 文档格式转换 ========== -->
<dependency> <dependency>
@@ -366,4 +372,4 @@
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

16
server/src/main/bin/dev.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/bash
set -e
ROOT_DIR=$(cd "$(dirname "$0")/../../../.." || exit 1; pwd)
SERVER_DIR="$ROOT_DIR/server"
if [ -n "$JAVA_HOME" ]; then
export PATH="$JAVA_HOME/bin:$PATH"
fi
cd "$SERVER_DIR" || exit 1
mvn spring-boot:run \
-Dspring-boot.run.addResources=true \
-Dspring-boot.run.jvmArguments="-Dfile.encoding=UTF-8 -Dspring.config.location=$SERVER_DIR/src/main/config/application.properties"

View File

@@ -1,10 +1,20 @@
@echo off @echo off
set "KKFILEVIEW_BIN_FOLDER=%cd%" set "KKFILEVIEW_BIN_FOLDER=%cd%"
cd "%KKFILEVIEW_BIN_FOLDER%" cd "%KKFILEVIEW_BIN_FOLDER%"
set "JAR_NAME="
for %%F in (kkFileView-*.jar) do (
set "JAR_NAME=%%~nxF"
goto :jar_found
)
echo Error: kkFileView jar not found in %KKFILEVIEW_BIN_FOLDER%
exit /b 1
:jar_found
echo Using KKFILEVIEW_BIN_FOLDER %KKFILEVIEW_BIN_FOLDER% echo Using KKFILEVIEW_BIN_FOLDER %KKFILEVIEW_BIN_FOLDER%
echo Using JAR_NAME %JAR_NAME%
echo Starting kkFileView... echo Starting kkFileView...
echo Please check log file in ../log/kkFileView.log for more information echo Please check log file in ../log/kkFileView.log for more information
echo You can get help in our official home site: https://kkview.cn echo You can get help in our official home site: https://kkview.cn
echo If you need further help, please join our kk opensource community: https://t.zsxq.com/09ZHSXbsQ echo If you need further help, please join our kk opensource community: https://t.zsxq.com/09ZHSXbsQ
echo If this project is helpful to you, please star it on https://gitee.com/kekingcn/file-online-preview/stargazers echo If this project is helpful to you, please star it on https://gitee.com/kekingcn/file-online-preview/stargazers
java -Dspring.config.location=..\config\application.properties -jar kkFileView-4.4.0.jar -> ..\log\kkFileView.log java -Dspring.config.location=..\config\application.properties -jar "%JAR_NAME%" > ..\log\kkFileView.log 2>&1

View File

@@ -49,9 +49,16 @@ else
fi fi
fi fi
JAR_PATH=$(ls kkFileView-*.jar 2>/dev/null | head -n 1)
if [ -z "${JAR_PATH}" ]; then
echo "kkFileView jar not found in ${KKFILEVIEW_BIN_FOLDER}"
exit 1
fi
## 启动kkFileView ## 启动kkFileView
echo "Starting kkFileView..." echo "Starting kkFileView..."
nohup java -Dfile.encoding=UTF-8 -Dspring.config.location=../config/application.properties -jar kkFileView-4.4.0.jar > ../log/kkFileView.log 2>&1 & echo "Using jar ${JAR_PATH}"
nohup java -Dfile.encoding=UTF-8 -Dspring.config.location=../config/application.properties -jar "${JAR_PATH}" > ../log/kkFileView.log 2>&1 &
echo "Please execute ./showlog.sh to check log for more information" echo "Please execute ./showlog.sh to check log for more information"
echo "You can get help in our official home site: https://kkview.cn" echo "You can get help in our official home site: https://kkview.cn"
echo "If you need further help, please join our kk opensource community: https://t.zsxq.com/09ZHSXbsQ" echo "If you need further help, please join our kk opensource community: https://t.zsxq.com/09ZHSXbsQ"

View File

@@ -96,12 +96,12 @@ office.documentopenpasswords = ${KK_OFFICE_DOCUMENTOPENPASSWORD:true}
office.type.web = ${KK_OFFICE_TYPE_WEB:web} office.type.web = ${KK_OFFICE_TYPE_WEB:web}
# Office文档预览类型 # Office文档预览类型
# 支持动态配置可选值image/pdf # 支持动态配置可选值image/pdf默认使用pdf模式
office.preview.type = ${KK_OFFICE_PREVIEW_TYPE:image} office.preview.type = ${KK_OFFICE_PREVIEW_TYPE:pdf}
# 是否关闭Office预览模式切换开关默认为false允许切换 # 是否关闭Office预览模式切换开关默认为true关闭切换
# 设置为true时用户无法在图片和PDF模式间切换 # 设置为false时用户可以在图片和PDF模式间切换
office.preview.switch.disabled = ${KK_OFFICE_PREVIEW_SWITCH_DISABLED:false} office.preview.switch.disabled = ${KK_OFFICE_PREVIEW_SWITCH_DISABLED:true}
############################################################################### ###############################################################################
@@ -475,4 +475,4 @@ kk.scriptjs = true
############################################################################### ###############################################################################
# 纯文本文件类型直接显示 # 纯文本文件类型直接显示
simText = ${KK_SIMTEXT:txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd} simText = ${KK_SIMTEXT:txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd}

View File

@@ -59,21 +59,26 @@ public class CompressFileReader {
for (final ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) { for (final ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
if (!item.isFolder()) { if (!item.isFolder()) {
final Path filePathInsideArchive = getFilePathInsideArchive(item, folderPath); final Path filePathInsideArchive = getFilePathInsideArchive(item, folderPath);
ExtractOperationResult result = item.extractSlow(data -> { Files.deleteIfExists(filePathInsideArchive);
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(filePathInsideArchive.toFile(), true))) { try (OutputStream out = new BufferedOutputStream(new FileOutputStream(filePathInsideArchive.toFile(), false))) {
out.write(data); ExtractOperationResult result = item.extractSlow(data -> {
} catch (IOException e) { try {
throw new RuntimeException(e); out.write(data);
} } catch (IOException e) {
return data.length; throw new RuntimeException(e);
}, filePassword); }
if (result != ExtractOperationResult.OK) { return data.length;
ExtractOperationResult result1 = ExtractOperationResult.valueOf("WRONG_PASSWORD"); }, filePassword);
if (result1.equals(result)) { if (result != ExtractOperationResult.OK) {
throw new Exception("Password"); ExtractOperationResult result1 = ExtractOperationResult.valueOf("WRONG_PASSWORD");
}else { if (result1.equals(result)) {
throw new Exception("Failed to extract RAR file."); throw new Exception("Password");
} else {
throw new Exception("Failed to extract RAR file.");
}
} }
} catch (IOException e) {
throw new RuntimeException(e);
} }
FileType type = FileType.typeFromUrl(filePathInsideArchive.toString()); FileType type = FileType.typeFromUrl(filePathInsideArchive.toString());
@@ -110,4 +115,4 @@ public class CompressFileReader {
} }
} }

View File

@@ -348,4 +348,4 @@ public class OfficeFilePreviewImpl implements FilePreview {
} }
return null; return null;
} }
} }

View File

@@ -31,6 +31,11 @@ public class IndexController {
return "/main/integrated"; return "/main/integrated";
} }
@GetMapping( "/contact")
public String go2Contact(){
return "/main/contact";
}
@GetMapping( "/") @GetMapping( "/")
public String root() { public String root() {
return "/main/index"; return "/main/index";

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Microsoft Excel</title><path d="M23 1.5q.41 0 .7.3.3.29.3.7v19q0 .41-.3.7-.29.3-.7.3H7q-.41 0-.7-.3-.3-.29-.3-.7V18H1q-.41 0-.7-.3-.3-.29-.3-.7V7q0-.41.3-.7Q.58 6 1 6h5V2.5q0-.41.3-.7.29-.3.7-.3zM6 13.28l1.42 2.66h2.14l-2.38-3.87 2.34-3.8H7.46l-1.3 2.4-.05.08-.04.09-.64-1.28-.66-1.29H2.59l2.27 3.82-2.48 3.85h2.16zM14.25 21v-3H7.5v3zm0-4.5v-3.75H12v3.75zm0-5.25V7.5H12v3.75zm0-5.25V3H7.5v3zm8.25 15v-3h-6.75v3zm0-4.5v-3.75h-6.75v3.75zm0-5.25V7.5h-6.75v3.75zm0-5.25V3h-6.75v3Z"/></svg>

After

Width:  |  Height:  |  Size: 563 B

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Adobe Acrobat Reader</title><path d="M23.63 15.3c-.71-.745-2.166-1.17-4.224-1.17-1.1 0-2.377.106-3.761.354a19.443 19.443 0 0 1-2.307-2.661c-.532-.71-.994-1.49-1.42-2.236.817-2.484 1.207-4.507 1.207-5.962 0-1.632-.603-3.336-2.342-3.336-.532 0-1.065.32-1.349.781-.78 1.384-.425 4.4.923 7.381a60.277 60.277 0 0 1-1.703 4.507c-.568 1.349-1.207 2.733-1.917 4.01C2.834 18.53.314 20.34.03 21.758c-.106.533.071 1.03.462 1.42.142.107.639.533 1.49.533 2.59 0 5.323-4.188 6.707-6.707 1.065-.355 2.13-.71 3.194-.994a34.963 34.963 0 0 1 3.407-.745c2.732 2.448 5.145 2.839 6.352 2.839 1.49 0 2.023-.604 2.2-1.1.32-.64.106-1.349-.213-1.704zm-1.42 1.03c-.107.532-.64.887-1.384.887-.213 0-.39-.036-.604-.071-1.348-.32-2.626-.994-3.903-2.059a17.717 17.717 0 0 1 2.98-.248c.746 0 1.385.035 1.81.142.497.106 1.278.426 1.1 1.348zm-7.524-1.668a38.01 38.01 0 0 0-2.945.674 39.68 39.68 0 0 0-2.52.745 40.05 40.05 0 0 0 1.207-2.555c.426-.994.78-2.023 1.136-2.981.354.603.745 1.207 1.135 1.739a50.127 50.127 0 0 0 1.987 2.378zM10.038 1.46a.768.768 0 0 1 .674-.425c.745 0 .887.851.887 1.526 0 1.135-.355 2.874-.958 4.861-1.03-2.768-1.1-5.074-.603-5.962zM6.134 17.997c-1.81 2.981-3.549 4.826-4.613 4.826a.872.872 0 0 1-.532-.177c-.213-.213-.32-.461-.249-.745.213-1.065 2.271-2.555 5.394-3.904Z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Microsoft PowerPoint</title><path d="M13.5 1.5q1.453 0 2.795.375 1.342.375 2.508 1.06 1.166.686 2.12 1.641.956.955 1.641 2.121.686 1.166 1.061 2.508Q24 10.547 24 12q0 1.453-.375 2.795-.375 1.342-1.06 2.508-.686 1.166-1.641 2.12-.955.956-2.121 1.641-1.166.686-2.508 1.061-1.342.375-2.795.375-1.29 0-2.52-.305-1.23-.304-2.337-.884-1.108-.58-2.063-1.418-.955-.838-1.693-1.893H.997q-.411 0-.704-.293T0 17.004V6.996q0-.41.293-.703T.996 6h3.89q.739-1.055 1.694-1.893.955-.837 2.063-1.418 1.107-.58 2.337-.884Q12.21 1.5 13.5 1.5zm.75 1.535v8.215h8.215q-.14-1.64-.826-3.076-.686-1.436-1.782-2.531-1.095-1.096-2.537-1.782-1.441-.685-3.07-.826zm-5.262 7.57q0-.68-.228-1.166-.229-.486-.627-.79-.399-.305-.938-.446-.539-.14-1.172-.14H2.848v7.863h1.84v-2.742H5.93q.574 0 1.119-.17t.978-.493q.434-.322.698-.802.263-.48.263-1.114zM13.5 21q1.172 0 2.262-.287t2.056-.82q.967-.534 1.776-1.278.808-.744 1.418-1.664.61-.92.984-1.986.375-1.067.469-2.227h-9.703V3.035q-1.735.14-3.27.908T6.797 6h4.207q.41 0 .703.293t.293.703v10.008q0 .41-.293.703t-.703.293H6.797q.644.715 1.412 1.271.768.557 1.623.944.855.387 1.781.586Q12.54 21 13.5 21zM5.812 9.598q.575 0 .915.228.34.229.34.838 0 .27-.124.44-.123.17-.31.275-.188.105-.422.146-.234.041-.445.041H4.687V9.598Z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Microsoft Word</title><path d="M23.004 1.5q.41 0 .703.293t.293.703v19.008q0 .41-.293.703t-.703.293H6.996q-.41 0-.703-.293T6 21.504V18H.996q-.41 0-.703-.293T0 17.004V6.996q0-.41.293-.703T.996 6H6V2.496q0-.41.293-.703t.703-.293zM6.035 11.203l1.442 4.735h1.64l1.57-7.876H9.036l-.937 4.653-1.325-4.5H5.38l-1.406 4.523-.938-4.675H1.312l1.57 7.874h1.641zM22.5 21v-3h-15v3zm0-4.5v-3.75H12v3.75zm0-5.25V7.5H12v3.75zm0-5.25V3h-15v3Z"/></svg>

After

Width:  |  Height:  |  Size: 510 B

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,104 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>kkFileView 技术支持</title>
<link rel="icon" href="./favicon.ico" type="image/x-icon">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;600&family=Space+Grotesk:wght@500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"/>
<link rel="stylesheet" href="css/theme.css"/>
<link rel="stylesheet" href="css/main-pages.css"/>
<script type="text/javascript" src="js/jquery-3.6.1.min.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
</head>
<body class="app-shell">
<nav class="site-nav navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="https://kkview.cn" target="_blank">kkFileView</a>
</div>
<ul class="nav navbar-nav">
<li><a href="./index">首页</a></li>
<li><a href="./integrated">接入说明</a></li>
<li><a href="./record">版本发布记录</a></li>
<li><a href="./sponsor">赞助开源</a></li>
<li class="active"><a href="./contact">技术支持</a></li>
</ul>
</div>
</nav>
<div class="page-shell">
<div class="container" role="main">
<section class="hero-section release-hero">
<div class="hero-copy">
<div class="contact-hero-layout">
<div class="contact-hero-copy">
<span class="eyebrow">Contact Us</span>
<h1 class="hero-title">技术支持</h1>
<p class="hero-subtitle">
如果你在部署、安装、接入或日常使用 kkFileView 时需要更直接的支持,
可以加入我们的付费知识星球,获取安装使用技术支持。
</p>
<div class="hero-actions">
<a class="hero-link primary" href="https://wx.zsxq.com/group/48844125114258" target="_blank">加入知识星球</a>
<a class="hero-link secondary" href="./integrated">查看接入说明</a>
</div>
</div>
<div class="contact-hero-side">
<div class="support-summary">
<div class="support-summary-item">
<span class="tag brand">安装支持</span>
<p>提供最新的安装发行包,公示安全补丁等。</p>
</div>
<div class="support-summary-item">
<span class="tag">使用咨询</span>
<p>围绕接入、配置和日常使用问题提供支持。</p>
</div>
<div class="support-summary-item">
<span class="tag highlight">付费知识星球</span>
<p>通过知识星球联系,我们提供更直接的技术支持。</p>
</div>
</div>
</div>
</div>
</div>
</section>
<div class="sponsor-grid">
<section class="doc-card">
<div class="doc-card-header">
<div>
<span class="eyebrow">Support Scope</span>
<h3>支持内容</h3>
</div>
</div>
<p>如果你希望更快完成部署和落地,可以通过知识星球联系我们,获得更直接的安装使用支持。</p>
<ul>
<li>安装部署相关问题。</li>
<li>配置、接入和常见使用问题。</li>
<li>围绕实际使用场景的排查与建议。</li>
</ul>
</section>
<section class="doc-card">
<div class="doc-card-header">
<div>
<span class="eyebrow">Knowledge Planet</span>
<h3>加入方式</h3>
</div>
</div>
<p>知识星球地址:</p>
<p><a href="https://wx.zsxq.com/group/48844125114258" target="_blank">https://wx.zsxq.com/group/48844125114258</a></p>
<div class="note-row">
<span class="tag brand"><a href="https://wx.zsxq.com/group/48844125114258" target="_blank">立即加入</a></span>
</div>
</section>
</div>
</div>
</div>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -1,118 +1,254 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en">
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>接入说明</title> <title>kkFileView 接入说明</title>
<link rel="icon" href="./favicon.ico" type="image/x-icon"> <link rel="icon" href="./favicon.ico" type="image/x-icon">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;600&family=Space+Grotesk:wght@500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"/> <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"/>
<link rel="stylesheet" href="css/theme.css"/> <link rel="stylesheet" href="css/theme.css"/>
<link rel="stylesheet" href="css/main-pages.css"/>
<script type="text/javascript" src="js/jquery-3.6.1.min.js"></script> <script type="text/javascript" src="js/jquery-3.6.1.min.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script> <script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="highlight/highlight.min.js"></script>
</head> </head>
<body> <body class="app-shell">
<nav class="site-nav navbar navbar-inverse navbar-fixed-top">
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container"> <div class="container">
<div class="navbar-header"> <div class="navbar-header">
<a class="navbar-brand" href="https://kkview.cn" target='_blank'>kkFileView</a> <a class="navbar-brand" href="https://kkview.cn" target="_blank">kkFileView</a>
</div> </div>
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li><a href="./index">首页</a></li> <li><a href="./index">首页</a></li>
<li class="active"><a href="./integrated">接入说明</a></li> <li class="active"><a href="./integrated">接入说明</a></li>
<li><a href="./record">版本发布记录</a></li> <li><a href="./record">版本发布记录</a></li>
<li><a href="./sponsor">赞助开源</a></li> <li><a href="./sponsor">赞助开源</a></li>
<li><a href="./contact">技术支持</a></li>
</ul> </ul>
</div> </div>
</nav> </nav>
<div class="container theme-showcase" role="main"> <div class="page-shell">
<#-- 接入说明 --> <div class="container" role="main">
<div class="page-header"> <section class="hero-section release-hero">
<h1>接入说明</h1> <div class="hero-copy">
本文档针对前端项目接入 kkFileView 的说明,并假设 kkFileView 的服务地址为http://127.0.0.1:8012。 <span class="eyebrow">Integration Guide</span>
</div> <h1 class="hero-title">5 分钟把 kkFileView 接进你的业务项目。</h1>
<div class="well"> <p class="hero-subtitle hero-subtitle-inline">
这里按常见接入场景提供示例,方便你直接按需选用。默认假设服务地址为 <span class="text-highlight">${baseUrl}</span>。
</p>
<div class="note-row">
<span class="tag brand">HTTP / HTTPS</span>
<span class="tag">FTP</span>
<span class="tag highlight">AES</span>
<span class="tag warn">附加参数</span>
</div>
<div class="summary-grid">
<div class="summary-panel">
<strong>URL</strong>
<span>所有预览能力统一汇总到 `onlinePreview` 入口。</span>
</div>
<div class="summary-panel">
<strong>Base64</strong>
<span>普通接入默认对原始文件地址做 Base64 编码后再传入。</span>
</div>
<div class="summary-panel">
<strong>参数扩展</strong>
<span>支持页码、高亮、水印、密码、跨域、AES 和秘钥等控制项。</span>
</div>
</div>
</div>
</section>
<div style="font-size: 16px;"> <div class="docs-layout">
【http/https 资源文件预览】如果你的项目需要接入文件预览项目达到对docx、excel、ppt、jpg等文件的预览效果那么通过在你的项目中加入下面的代码就可以成功实现 <aside class="page-toc">
<p style="background-color: #2f332a;color: #cccccc;font-size: 14px;padding:10px;margin-top:10px;"> <h3>快速导航</h3>
var url = 'http://127.0.0.1:8080/file/test.txt'; //要预览文件的访问地址 <br> <ul>
window.open('${baseUrl}onlinePreview?url='+encodeURIComponent(base64Encode(url))); <li><a href="#quick-start">快速开始</a></li>
</p> <li><a href="#http-preview">HTTP 文件预览</a></li>
</div> <li><a href="#stream-preview">流式接口预览</a></li>
<br> <li><a href="#ftp-preview">FTP 预览</a></li>
<div style="font-size: 16px;"> <li><a href="#basic-auth">Basic 鉴权</a></li>
【http/https 流资源文件预览】很多系统内不是直接暴露文件下载地址而是请求通过id、code等参数到通过统一的接口后端通过id或code等参数定位文件再通过OutputStream输出下载此时下载url是不带文件后缀名的预览时需要拿到文件名传一个参数fullfilename=xxx.xxx来指定文件名示例如下 <li><a href="#aes-preview">AES 加密</a></li>
<p style="background-color: #2f332a;color: #cccccc;font-size: 14px;padding:10px;margin-top:10px;"> <li><a href="#extra-params">附加参数</a></li>
var originUrl = 'http://127.0.0.1:8080/filedownload?fileId=1'; //要预览文件的访问地址<br> </ul>
var previewUrl = originUrl + '&fullfilename=test.txt'<br> </aside>
window.open('${baseUrl}onlinePreview?url='+encodeURIComponent(Base64.encode(previewUrl)));
</p> <div class="docs-content">
</div> <section class="doc-card" id="quick-start">
<br> <div class="doc-card-header">
<div style="font-size: 16px;"> <div>
【ftp 资源文件预览】如果要预览的FTP url是可以匿名访问的不需要用户名密码则可以直接通过下载url预览示例如下 <span class="eyebrow">Quick Start</span>
<p style="background-color: #2f332a;color: #cccccc;font-size: 14px;padding:10px;margin-top:10px;"> <h3>接入思路</h3>
var url = 'ftp://127.0.0.1/file/test.txt'; //要预览文件的访问地址<br> </div>
window.open('${baseUrl}onlinePreview?url='+encodeURIComponent(Base64.encode(url))); <div class="tags">
</p> <span class="tag brand">推荐入口</span>
</div> </div>
<br> </div>
<div style="font-size: 16px;"> <p>前端只需要拿到可访问的文件 URL然后把它编码后拼到 `${baseUrl}onlinePreview` 上即可。对于大多数业务系统,这是最快的落地路径。</p>
【ftp 加密资源文件预览】如果 FTP 需要认证访问服,可以通过在 url 中加入用户名密码等参数预览,示例如下 <ul>
<p style="background-color: #2f332a;color: #cccccc;font-size: 14px;padding:10px;margin-top:10px;"> <li>普通 HTTP/HTTPS 文件地址:直接 Base64 编码后传入。</li>
var originUrl = 'ftp://127.0.0.1/file/test.txt'; //要预览文件的访问地址<br> <li>下载流接口没有后缀名:补充 `fullfilename=xxx.xxx`。</li>
var previewUrl = originUrl + '?ftp.control.port=xx&ftp.username=xx&ftp.password=xx&ftp.control.encoding=(gbk,utf8等)'; //(为了安全强烈建议在配置中设置相关信息)<br> <li>鉴权或加密场景:附加 Basic、FTP、AES 等参数。</li>
window.open('${baseUrl}onlinePreview?url='+encodeURIComponent(Base64.encode(previewUrl))); </ul>
</p> </section>
</div>
<div style="font-size: 16px;"> <section class="doc-card" id="http-preview">
【Basic 鉴权资源文件预览】如果需要认证访问服可以通过在url中加入用户名密码等参数预览示例如下 <div class="doc-card-header">
<p style="background-color: #2f332a;color: #cccccc;font-size: 14px;padding:10px;margin-top:10px;"> <div>
var originUrl = 'http://127.0.0.1/file/test.txt'; //要预览文件的访问地址<br> <span class="eyebrow">HTTP / HTTPS</span>
var previewUrl = originUrl + '?basic.name=admin&basic.pass=123456'; //(为了安全强烈建议在配置中设置相关信息)<br> <h3>普通文件地址预览</h3>
window.open('${baseUrl}onlinePreview?url='+encodeURIComponent(Base64.encode(previewUrl))); </div>
</p> <button class="copy-btn" type="button" onclick="copyCode(this)">复制代码</button>
</div> </div>
<div style="font-size: 16px;"> <p>适用于系统已经直接暴露出可下载文件地址的情况。前端只需要编码后打开新窗口。</p>
AES加密接入方法示例如下 <div class="code-block">
<p style="background-color: #2f332a;color: #cccccc;font-size: 14px;padding:10px;margin-top:10px;"> <code class="language-javascript">var url = 'http://127.0.0.1:8080/file/test.txt';
主要事项首先引入下面js 在把url转换成AES,注意前后端key必须相同注意JS下载到你接入服务器的网址<br> window.open('${baseUrl}onlinePreview?url=' + encodeURIComponent(base64Encode(url)));</code>
&lt;script src="${baseUrl}js/crypto-js.js"></script><br> </div>
&lt;script src="${baseUrl}js/aes.js"></script><br> </section>
function aesEncrypt(encryptString, key) { <br>
var key = CryptoJS.enc.Utf8.parse(key); <br> <section class="doc-card" id="stream-preview">
var srcs = CryptoJS.enc.Utf8.parse(encryptString); <br> <div class="doc-card-header">
var encrypted = CryptoJS.AES.encrypt(srcs, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }); <br> <div>
return encrypted.toString(); <br> <span class="eyebrow">Streaming</span>
}<br> <h3>流式接口预览</h3>
var key = "1234567890123456"; // AES秘钥16位数字<br> </div>
var url = "http://127.0.0.1/file/test.txt";<br> <button class="copy-btn" type="button" onclick="copyCode(this)">复制代码</button>
window.open('${baseUrl}onlinePreview?url='+encodeURIComponent(aesEncrypt(url, key))+'&encryption=aes'); </div>
</p> <p>很多业务系统通过 `fileId`、`code` 等参数走统一下载接口,此时原始 URL 没有后缀名,需要额外指定完整文件名。</p>
</div> <div class="code-block">
<code class="language-javascript">var originUrl = 'http://127.0.0.1:8080/filedownload?fileId=1';
<div style="font-size: 16px;"> var previewUrl = originUrl + '&amp;fullfilename=test.txt';
其他参数示例如下 window.open('${baseUrl}onlinePreview?url=' + encodeURIComponent(Base64.encode(previewUrl)));</code>
<p style="background-color: #2f332a;color: #cccccc;font-size: 14px;padding:10px;margin-top:10px;"> </div>
密码参数&filePassword=加密文件的密码<br> </section>
页码参数&page=选择第几页预览<br>
高亮参数&highlightall=关键字 突出显示 <br> <section class="doc-card" id="ftp-preview">
水印参数&watermarkTxt=你的水印<br> <div class="doc-card-header">
重生参数&forceUpdatedCache=true <br> <div>
跨域参数&kkagent=true <br> <span class="eyebrow">FTP</span>
加密缓存&usePasswordCache=true <br> <h3>FTP 资源预览</h3>
秘钥参数&key= 访问秘钥 <br> </div>
主要事项以上参数是把url转换成base64后面在添加<br> <button class="copy-btn" type="button" onclick="copyCode(this)">复制代码</button>
var url = 'http://127.0.0.1:8080/file/test.txt'<br> </div>
window.open('${baseUrl}onlinePreview?url='+encodeURIComponent(base64Encode(url))+'&filePassword=123&page=1&highlightall=kkfileview&watermarkTxt=kkfileview&kkagent=false&key=123'); <p>FTP 如果允许匿名访问,可以直接预览;如果需要认证,则把连接参数拼到 URL 后面传入。</p>
</p> <div class="code-block">
<code class="language-javascript">// 匿名 FTP
var url = 'ftp://127.0.0.1/file/test.txt';
window.open('${baseUrl}onlinePreview?url=' + encodeURIComponent(Base64.encode(url)));
// 认证 FTP
var originUrl = 'ftp://127.0.0.1/file/test.txt';
var previewUrl = originUrl + '?ftp.control.port=21&amp;ftp.username=admin&amp;ftp.password=123456&amp;ftp.control.encoding=utf8';
window.open('${baseUrl}onlinePreview?url=' + encodeURIComponent(Base64.encode(previewUrl)));</code>
</div>
</section>
<section class="doc-card" id="basic-auth">
<div class="doc-card-header">
<div>
<span class="eyebrow">Basic Auth</span>
<h3>带 Basic 鉴权的 HTTP 资源</h3>
</div>
<button class="copy-btn" type="button" onclick="copyCode(this)">复制代码</button>
</div>
<p>如果文件源本身需要用户名和密码,可以直接把 Basic 鉴权参数拼到地址中,再交给 kkFileView。</p>
<div class="code-block">
<code class="language-javascript">var originUrl = 'http://127.0.0.1/file/test.txt';
var previewUrl = originUrl + '?basic.name=admin&amp;basic.pass=123456';
window.open('${baseUrl}onlinePreview?url=' + encodeURIComponent(Base64.encode(previewUrl)));</code>
</div>
</section>
<section class="doc-card" id="aes-preview">
<div class="doc-card-header">
<div>
<span class="eyebrow">AES</span>
<h3>前后端同秘钥加密接入</h3>
</div>
<button class="copy-btn" type="button" onclick="copyCode(this)">复制代码</button>
</div>
<p>如果不希望明文传递原始文件地址,可以在前端先做 AES 加密,再通过 `encryption=aes` 告知服务端按 AES 方式解密。</p>
<div class="code-block">
<code class="language-javascript">&lt;script src="${baseUrl}js/crypto-js.js"&gt;&lt;/script&gt;
&lt;script src="${baseUrl}js/aes.js"&gt;&lt;/script&gt;
function aesEncrypt(encryptString, key) {
var keyBytes = CryptoJS.enc.Utf8.parse(key);
var srcs = CryptoJS.enc.Utf8.parse(encryptString);
var encrypted = CryptoJS.AES.encrypt(srcs, keyBytes, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
}
var key = '1234567890123456';
var url = 'http://127.0.0.1/file/test.txt';
window.open('${baseUrl}onlinePreview?url=' + encodeURIComponent(aesEncrypt(url, key)) + '&amp;encryption=aes');</code>
</div>
</section>
<section class="doc-card" id="extra-params">
<div class="doc-card-header">
<div>
<span class="eyebrow">Parameters</span>
<h3>常用附加参数</h3>
</div>
<button class="copy-btn" type="button" onclick="copyCode(this)">复制代码</button>
</div>
<p>这些参数都应该在原始 URL 编码完成之后,再附加到预览地址后面。</p>
<ul>
<li>`filePassword`:加密文件的密码。</li>
<li>`page`:指定预览页码。</li>
<li>`highlightall`:关键字高亮。</li>
<li>`watermarkTxt`:动态水印文本。</li>
<li>`forceUpdatedCache=true`:强制刷新缓存。</li>
<li>`kkagent=true`:需要 kkFileView 代理跨域时启用。</li>
<li>`usePasswordCache=true`:开启密码缓存。</li>
<li>`key`:实例启用秘钥后传入访问秘钥。</li>
</ul>
<div class="code-block">
<code class="language-javascript">var url = 'http://127.0.0.1:8080/file/test.txt';
window.open(
'${baseUrl}onlinePreview?url=' +
encodeURIComponent(base64Encode(url)) +
'&amp;filePassword=123' +
'&amp;page=1' +
'&amp;highlightall=kkfileview' +
'&amp;watermarkTxt=kkfileview' +
'&amp;kkagent=false' +
'&amp;key=123'
);</code>
</div>
</section>
</div>
</div> </div>
</div> </div>
</div> </div>
<script>
if (window.hljs) {
document.querySelectorAll('.code-block code').forEach(function (block) {
hljs.highlightBlock(block);
});
}
function copyCode(button) {
var code = button.parentNode.parentNode.querySelector('code').innerText;
var originalText = button.textContent;
navigator.clipboard.writeText(code).then(function () {
button.textContent = '已复制';
setTimeout(function () {
button.textContent = originalText;
}, 1500);
});
}
</script>
</body> </body>
</html> </html>

File diff suppressed because it is too large Load Diff

View File

@@ -1,72 +1,127 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en">
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>赞助开源</title> <title>kkFileView 赞助开源</title>
<link rel="icon" href="./favicon.ico" type="image/x-icon"> <link rel="icon" href="./favicon.ico" type="image/x-icon">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;600&family=Space+Grotesk:wght@500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"/> <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"/>
<link rel="stylesheet" href="css/theme.css"/> <link rel="stylesheet" href="css/theme.css"/>
<link rel="stylesheet" href="css/main-pages.css"/>
<script type="text/javascript" src="js/jquery-3.6.1.min.js"></script> <script type="text/javascript" src="js/jquery-3.6.1.min.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script> <script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
</head> </head>
<body> <body class="app-shell">
<nav class="site-nav navbar navbar-inverse navbar-fixed-top">
<!-- Fixed navbar -->
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container"> <div class="container">
<div class="navbar-header"> <div class="navbar-header">
<a class="navbar-brand" href="https://kkview.cn" target='_blank'>kkFileView</a> <a class="navbar-brand" href="https://kkview.cn" target="_blank">kkFileView</a>
</div> </div>
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li><a href="./index">首页</a></li> <li><a href="./index">首页</a></li>
<li><a href="./integrated">接入说明</a></li> <li><a href="./integrated">接入说明</a></li>
<li><a href="./record">版本发布记录</a></li> <li><a href="./record">版本发布记录</a></li>
<li class="active"><a href="./sponsor">赞助开源</a></li> <li class="active"><a href="./sponsor">赞助开源</a></li>
<li><a href="./contact">技术支持</a></li>
</ul> </ul>
</div> </div>
</nav> </nav>
<div class="container theme-showcase" role="main"> <div class="page-shell">
<#-- 接入说明 --> <div class="container" role="main">
<div class="page-header"> <section class="hero-section release-hero">
<h1>赞助开源</h1> <div class="hero-copy">
<ul style="font-size: 16px;"> <span class="eyebrow">Sponsor Open Source</span>
<li>kkFileView 开源至今已 6 个年头,积累 <a target="_blank" href="https://gitee.com/kekingcn/file-online-preview">Gitee(16.9K)</a>、<a target="_blank" href="https://github.com/kekingcn/kkFileView">GitHub(8k)</a> 多的 star</li> <h1 class="hero-title">赞助开源</h1>
<li>kkFileView 被广泛应用于金融教育银行政务计算机等行业, 不完全统计有 200+ 企业在使用</li> <p class="hero-subtitle">
<li>kkFileView 每年的文档站点演示站点的服务器CDN 资源, 至少在 1000元以上</li> kkFileView 已持续维护多年,被广泛用于金融、教育、银行、政务和企业内部系统。
<li>kkFileView 是一款完全开源的在线预览项目如果你觉得 kkFileView 对你有帮助可以通过下面的方式来赞助我们谢谢</li> 赞助会直接用于文档站、演示站、服务器和 CDN 等基础开销。
</ul> </p>
</div> <div class="summary-grid">
<div> <div class="summary-panel">
<div style="font-size: 16px; text-align: center;"> <strong>多年维护</strong>
<img width="400px" height="550px" alt="alipay" src="../images/alipay.jpeg"/> &nbsp;&nbsp;&nbsp;<img width="400px" height="550px" alt="wxpay" src="../images/wxpay.jpeg"/> <span>项目长期迭代,持续补格式、补安全、补性能。</span>
</div>
<div class="summary-panel">
<strong>广泛使用</strong>
<span>不完全统计已有 200+ 企业或团队在使用。</span>
</div>
<div class="summary-panel">
<strong>基础成本</strong>
<span>文档站、演示站和 CDN 等年成本至少在千元级。</span>
</div>
</div>
<div class="note-row">
<span class="tag brand"><a target="_blank" href="https://gitee.com/kekingcn/file-online-preview">Gitee</a></span>
<span class="tag brand"><a target="_blank" href="https://github.com/kekingcn/kkFileView">GitHub</a></span>
<span class="tag highlight">完全开源</span>
</div>
</div>
</section>
<div class="sponsor-grid">
<section class="doc-card">
<div class="doc-card-header">
<div>
<span class="eyebrow">Ways To Sponsor</span>
<h3>赞助方式</h3>
</div>
</div>
<p>如果你觉得 kkFileView 对你有帮助,可以通过下面的方式赞助项目,支持它继续长期维护。</p>
<ul>
<li>kkFileView 开源至今已多年,社区持续反馈并推动演进。</li>
<li>项目 star 与使用规模已经证明它不是“玩具 demo”而是被真实系统接入的基础能力。</li>
<li>赞助记录为手动录入,存在周级延迟;如有遗漏,可联系作者补录。</li>
</ul>
<div class="donation-wall">
<div class="qr-card">
<h4>支付宝</h4>
<img alt="支付宝赞助码" src="../images/alipay.jpeg"/>
</div>
<div class="qr-card">
<h4>微信支付</h4>
<img alt="微信赞助码" src="../images/wxpay.jpeg"/>
</div>
</div>
</section>
<section class="doc-card">
<div class="doc-card-header">
<div>
<span class="eyebrow">Sponsor Log</span>
<h3>赞助记录</h3>
</div>
<div class="tags">
<span class="tag">2023-03-14 开启赞助通道</span>
</div>
</div>
<p>赞助记录为手动维护,如有遗漏,请联系作者补充。</p>
<table class="table sponsor-table">
<thead>
<tr>
<th>赞助人</th>
<th>赞助金额</th>
<th>赞助时间</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>陈**</td>
<td class="sponsor-amount">99</td>
<td>2023-03-14</td>
<td>首批赞助记录</td>
</tr>
</tbody>
</table>
</section>
</div> </div>
</div> </div>
<div>
<h3>赞助记录</h3>
2023-03-14 开启赞助通道赞助记录为手动录入的存在周级别延迟如有遗漏请联系作者补充
<br/>
<table class="table table-striped table-bordered">
<tr>
<th>赞助人</th>
<th>赞助金额</th>
<th>赞助时间</th>
<th>备注</th>
</tr>
<tr>
<td>陈**</td>
<td>99</td>
<td>2023-03-14</td>
<td></td>
</tr>
</table>
</div>
</div> </div>
</body> </body>
</html> </html>

View File

@@ -27,7 +27,7 @@
if (kkagent === 'true' || !url.startsWith(baseUrl)) { if (kkagent === 'true' || !url.startsWith(baseUrl)) {
url = baseUrl + 'getCorsFile?urlPath=' + encodeURIComponent(Base64.encode(url))+ "&key=${kkkey}"; url = baseUrl + 'getCorsFile?urlPath=' + encodeURIComponent(Base64.encode(url))+ "&key=${kkkey}";
} }
document.getElementsByTagName('iframe')[0].src = "${baseUrl}pdfjs/web/viewer.html?file=" + encodeURIComponent(url) + "&disablepresentationmode=${pdfPresentationModeDisable}&disableopenfile=${pdfOpenFileDisable}&disableprint=${pdfPrintDisable}&disabledownload=${pdfDownloadDisable}&disablebookmark=${pdfBookmarkDisable}&disableediting=${pdfDisableEditing}"; document.getElementsByTagName('iframe')[0].src = "${baseUrl}pdfjs/web/viewer.html?file=" + encodeURIComponent(url) + "&disablepresentationmode=${pdfPresentationModeDisable}&disableopenfile=${pdfOpenFileDisable}&disableprint=${pdfPrintDisable}&disabledownload=${pdfDownloadDisable}&disablebookmark=${pdfBookmarkDisable}&disableediting=${pdfDisableEditing}#page=1&pagemode=thumbs";
document.getElementsByTagName('iframe')[0].height = document.documentElement.clientHeight - 10; document.getElementsByTagName('iframe')[0].height = document.documentElement.clientHeight - 10;
/** /**
* 页面变化调整高度 * 页面变化调整高度

View File

@@ -9,7 +9,15 @@
<script src="js/base64.min.js"></script> <script src="js/base64.min.js"></script>
<style> <style>
body { body {
background-color: #404040; background-color: #f1f3f5;
}
.viewer-container:focus {
outline: none !important;
}
.viewer-container:focus-visible {
outline: 2px solid rgba(95, 107, 122, 0.65) !important;
outline-offset: 2px;
box-shadow: 0 0 0 4px rgba(95, 107, 122, 0.14);
} }
#image { width: 800px; margin: 0 auto; font-size: 0;} #image { width: 800px; margin: 0 auto; font-size: 0;}
#image li { display: inline-block;width: 50px;height: 50px; margin-left: 1%; padding-top: 1%;} #image li { display: inline-block;width: 50px;height: 50px; margin-left: 1%; padding-top: 1%;}
@@ -77,4 +85,4 @@
} }
</script> </script>
</body> </body>
</html> </html>

View File

@@ -26,6 +26,20 @@ public class PdfViewerCompatibilityTests {
assertTrue(workerScript.contains("import \"../web/compatibility.mjs\";")); assertTrue(workerScript.contains("import \"../web/compatibility.mjs\";"));
} }
@Test
void shouldOpenPdfPreviewWithThumbnailSidebarByDefault() throws IOException {
String pdfTemplate = readResource("/web/pdf.ftl");
assertTrue(pdfTemplate.contains("#page=1&pagemode=thumbs"));
}
@Test
void shouldPreferPdfForOfficePreviewByDefault() throws IOException {
String properties = readResource("/application.properties");
assertTrue(properties.contains("office.preview.type = ${KK_OFFICE_PREVIEW_TYPE:pdf}"));
}
private String readResource(String resourcePath) throws IOException { private String readResource(String resourcePath) throws IOException {
try (InputStream inputStream = getClass().getResourceAsStream(resourcePath)) { try (InputStream inputStream = getClass().getResourceAsStream(resourcePath)) {
assertNotNull(inputStream); assertNotNull(inputStream);