编解码增加zlib
This commit is contained in:
66
deploy.sh
Executable file
66
deploy.sh
Executable file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 部署脚本:打包并通过 SSH 将构建产物同步到远程目录
|
||||||
|
# 用法: ./deploy.sh <服务器IP> <SSH端口> <登录账户> <登录密码> [sudo]
|
||||||
|
# 示例: ./deploy.sh 192.168.1.100 22 root mypassword
|
||||||
|
# 若远程目录无写权限,加第5个参数 sudo:先传到 /tmp,再通过 sudo 拷到目标(会用到同一密码作为 sudo 密码)
|
||||||
|
# 示例: ./deploy.sh 192.168.1.100 22 myuser mypassword sudo
|
||||||
|
|
||||||
|
if [ $# -lt 4 ]; then
|
||||||
|
echo "用法: $0 <服务器IP> <SSH端口> <登录账户> <登录密码> [sudo]"
|
||||||
|
echo "示例: $0 192.168.1.100 22 root mypassword"
|
||||||
|
echo "远程目录无写权限时加第5参数: $0 <IP> <端口> <用户> <密码> sudo"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
HOST="$1"
|
||||||
|
PORT="$2"
|
||||||
|
USER="$3"
|
||||||
|
PASSWORD="$4"
|
||||||
|
USE_SUDO="${5:-}"
|
||||||
|
REMOTE_DIR="/default/dockerfile/nginx/nginx/webapps/tool"
|
||||||
|
REMOTE_TMP="/tmp/tool_deploy_$$"
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
DIST_DIR="${SCRIPT_DIR}/dist"
|
||||||
|
SSH_OPTS="-p ${PORT} -o StrictHostKeyChecking=accept-new -T"
|
||||||
|
|
||||||
|
echo ">>> 正在打包..."
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
if [ ! -d "$DIST_DIR" ]; then
|
||||||
|
echo "错误: 构建产物目录 dist 不存在"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查 sshpass 是否可用(用于非交互式传入密码)
|
||||||
|
if ! command -v sshpass &> /dev/null; then
|
||||||
|
echo "未找到 sshpass,请先安装:"
|
||||||
|
echo " macOS: brew install sshpass"
|
||||||
|
echo " Ubuntu: sudo apt-get install sshpass"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export SSHPASS="$PASSWORD"
|
||||||
|
|
||||||
|
if [ "$USE_SUDO" = "sudo" ]; then
|
||||||
|
echo ">>> 正在同步到远程临时目录 ${REMOTE_TMP}"
|
||||||
|
sshpass -e rsync -avz --delete \
|
||||||
|
-e "ssh ${SSH_OPTS}" \
|
||||||
|
"${DIST_DIR}/" \
|
||||||
|
"${USER}@${HOST}:${REMOTE_TMP}/"
|
||||||
|
echo ">>> 正在用 sudo 拷贝到目标目录 ${REMOTE_DIR}"
|
||||||
|
# 只执行一次 sudo(读一次密码),在子 shell 里完成 rsync 与清理,避免第二次 sudo 无密码
|
||||||
|
printf '%s\n' "$PASSWORD" | sshpass -e ssh ${SSH_OPTS} "${USER}@${HOST}" \
|
||||||
|
"sudo -S sh -c 'rsync -avz --delete ${REMOTE_TMP}/ ${REMOTE_DIR}/ && rm -rf ${REMOTE_TMP}'"
|
||||||
|
else
|
||||||
|
echo ">>> 正在通过 SSH 同步到 ${USER}@${HOST}:${PORT} -> ${REMOTE_DIR}"
|
||||||
|
sshpass -e rsync -avz --delete \
|
||||||
|
-e "ssh ${SSH_OPTS}" \
|
||||||
|
"${DIST_DIR}/" \
|
||||||
|
"${USER}@${HOST}:${REMOTE_DIR}/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
unset SSHPASS
|
||||||
|
echo ">>> 部署完成"
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^7.1.0",
|
"@fortawesome/fontawesome-free": "^7.1.0",
|
||||||
|
"fflate": "^0.8.2",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"vue": "^3.4.0",
|
"vue": "^3.4.0",
|
||||||
"vue-router": "^4.2.5"
|
"vue-router": "^4.2.5"
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
item.encodingType === 'base64' ? 'Base64' :
|
item.encodingType === 'base64' ? 'Base64' :
|
||||||
item.encodingType === 'url' ? 'URL' :
|
item.encodingType === 'url' ? 'URL' :
|
||||||
item.encodingType === 'unicode' ? 'Unicode' :
|
item.encodingType === 'unicode' ? 'Unicode' :
|
||||||
|
item.encodingType === 'zlib' ? 'Zlib' :
|
||||||
item.encodingType
|
item.encodingType
|
||||||
}}</span>
|
}}</span>
|
||||||
<span class="history-time">{{ formatTime(item.time) }}</span>
|
<span class="history-time">{{ formatTime(item.time) }}</span>
|
||||||
@@ -77,6 +78,13 @@
|
|||||||
>
|
>
|
||||||
Unicode
|
Unicode
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
@click="encodingType = 'zlib'"
|
||||||
|
:class="['type-btn', { active: encodingType === 'zlib' }]"
|
||||||
|
title="Zlib 压缩/解压"
|
||||||
|
>
|
||||||
|
Zlib
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button @click="copyInputToClipboard" class="toolbar-icon-btn" title="复制">
|
<button @click="copyInputToClipboard" class="toolbar-icon-btn" title="复制">
|
||||||
<i class="far fa-copy"></i>
|
<i class="far fa-copy"></i>
|
||||||
@@ -151,10 +159,11 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch, onMounted, onUnmounted } from 'vue'
|
import { ref, watch, onMounted, onUnmounted } from 'vue'
|
||||||
|
import { zlibSync, decompressSync } from 'fflate'
|
||||||
|
|
||||||
const inputText = ref('')
|
const inputText = ref('')
|
||||||
const outputText = ref('')
|
const outputText = ref('')
|
||||||
const encodingType = ref('base64') // 'base64'、'url' 或 'unicode'
|
const encodingType = ref('base64') // 'base64'、'url'、'unicode' 或 'zlib'
|
||||||
const leftPanelWidth = ref(50)
|
const leftPanelWidth = ref(50)
|
||||||
const rightPanelWidth = ref(50)
|
const rightPanelWidth = ref(50)
|
||||||
const isResizing = ref(false)
|
const isResizing = ref(false)
|
||||||
@@ -219,6 +228,26 @@ watch(() => outputText.value, () => {
|
|||||||
updateOutputLineCount()
|
updateOutputLineCount()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Zlib: Uint8Array -> Base64(用于显示/复制)
|
||||||
|
const bytesToBase64 = (bytes) => {
|
||||||
|
let binary = ''
|
||||||
|
for (let i = 0; i < bytes.length; i++) {
|
||||||
|
binary += String.fromCharCode(bytes[i])
|
||||||
|
}
|
||||||
|
return btoa(binary)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zlib: Base64 -> Uint8Array(自动去除空白字符)
|
||||||
|
const base64ToBytes = (str) => {
|
||||||
|
const clean = str.replace(/\s/g, '')
|
||||||
|
const binary = atob(clean)
|
||||||
|
const bytes = new Uint8Array(binary.length)
|
||||||
|
for (let i = 0; i < binary.length; i++) {
|
||||||
|
bytes[i] = binary.charCodeAt(i)
|
||||||
|
}
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
|
||||||
// Unicode 编码
|
// Unicode 编码
|
||||||
const encodeUnicode = (text) => {
|
const encodeUnicode = (text) => {
|
||||||
let result = ''
|
let result = ''
|
||||||
@@ -321,6 +350,11 @@ const encode = () => {
|
|||||||
result = encodeURIComponent(inputText.value)
|
result = encodeURIComponent(inputText.value)
|
||||||
} else if (encodingType.value === 'unicode') {
|
} else if (encodingType.value === 'unicode') {
|
||||||
result = encodeUnicode(inputText.value)
|
result = encodeUnicode(inputText.value)
|
||||||
|
} else if (encodingType.value === 'zlib') {
|
||||||
|
const utf8 = new TextEncoder().encode(inputText.value)
|
||||||
|
// 使用 zlib 格式,与 Java Deflater/Inflater 默认格式兼容
|
||||||
|
const compressed = zlibSync(utf8)
|
||||||
|
result = bytesToBase64(compressed)
|
||||||
}
|
}
|
||||||
outputText.value = result
|
outputText.value = result
|
||||||
|
|
||||||
@@ -352,6 +386,11 @@ const decode = () => {
|
|||||||
result = decodeURIComponent(inputText.value)
|
result = decodeURIComponent(inputText.value)
|
||||||
} else if (encodingType.value === 'unicode') {
|
} else if (encodingType.value === 'unicode') {
|
||||||
result = decodeUnicode(inputText.value)
|
result = decodeUnicode(inputText.value)
|
||||||
|
} else if (encodingType.value === 'zlib') {
|
||||||
|
const compressed = base64ToBytes(inputText.value.trim())
|
||||||
|
// decompressSync 自动识别 zlib / raw deflate / gzip,可解 Java Deflater 输出
|
||||||
|
const decompressed = decompressSync(compressed)
|
||||||
|
result = new TextDecoder().decode(decompressed)
|
||||||
}
|
}
|
||||||
outputText.value = result
|
outputText.value = result
|
||||||
|
|
||||||
@@ -368,6 +407,7 @@ const decode = () => {
|
|||||||
const typeName = encodingType.value === 'base64' ? 'Base64' :
|
const typeName = encodingType.value === 'base64' ? 'Base64' :
|
||||||
encodingType.value === 'url' ? 'URL' :
|
encodingType.value === 'url' ? 'URL' :
|
||||||
encodingType.value === 'unicode' ? 'Unicode' :
|
encodingType.value === 'unicode' ? 'Unicode' :
|
||||||
|
encodingType.value === 'zlib' ? 'Zlib' :
|
||||||
encodingType.value
|
encodingType.value
|
||||||
showToast(`解码失败:请检查输入是否为有效的${typeName}编码字符串`)
|
showToast(`解码失败:请检查输入是否为有效的${typeName}编码字符串`)
|
||||||
outputText.value = ''
|
outputText.value = ''
|
||||||
|
|||||||
Reference in New Issue
Block a user