修复安卓手机显示名称不符显示为linux的问题;修复文件名乱码的问题
This commit is contained in:
@@ -456,6 +456,7 @@ func (h *HTTPHandler) downloadFallback(c *gin.Context) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
filename := filepath.Base(transfer.Name)
|
filename := filepath.Base(transfer.Name)
|
||||||
|
filename = decodeDownloadFilename(filename)
|
||||||
if filename == "." || filename == "" {
|
if filename == "." || filename == "" {
|
||||||
filename = "download.bin"
|
filename = "download.bin"
|
||||||
}
|
}
|
||||||
@@ -597,6 +598,19 @@ func contentDisposition(filename string) string {
|
|||||||
return `attachment; filename="` + escaped + `"`
|
return `attachment; filename="` + escaped + `"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodeDownloadFilename(filename string) string {
|
||||||
|
filename = strings.TrimSpace(filename)
|
||||||
|
if filename == "" || !strings.Contains(filename, "%") {
|
||||||
|
return filename
|
||||||
|
}
|
||||||
|
|
||||||
|
decoded, err := url.PathUnescape(filename)
|
||||||
|
if err != nil {
|
||||||
|
return filename
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(decoded)
|
||||||
|
}
|
||||||
|
|
||||||
func (h *HTTPHandler) ensureFallbackBucket(ctx context.Context, transferID string) error {
|
func (h *HTTPHandler) ensureFallbackBucket(ctx context.Context, transferID string) error {
|
||||||
if h.deps.MinIOClient == nil {
|
if h.deps.MinIOClient == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
17
frontend/dist/assets/index-BoiTbES-.js
vendored
Normal file
17
frontend/dist/assets/index-BoiTbES-.js
vendored
Normal file
File diff suppressed because one or more lines are too long
17
frontend/dist/assets/index-DPzeYqvr.js
vendored
17
frontend/dist/assets/index-DPzeYqvr.js
vendored
File diff suppressed because one or more lines are too long
2
frontend/dist/index.html
vendored
2
frontend/dist/index.html
vendored
@@ -4,7 +4,7 @@
|
|||||||
<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>AirShare Pro</title>
|
<title>AirShare Pro</title>
|
||||||
<script type="module" crossorigin src="/assets/index-DPzeYqvr.js"></script>
|
<script type="module" crossorigin src="/assets/index-BoiTbES-.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-C-7tVt-S.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-C-7tVt-S.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -302,6 +302,7 @@ async function loadPendingDownloads() {
|
|||||||
const downloads = await devicesApi.listPendingDownloads(localDevice.value.id)
|
const downloads = await devicesApi.listPendingDownloads(localDevice.value.id)
|
||||||
pendingDownloads.value = downloads.map((item) => ({
|
pendingDownloads.value = downloads.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
|
name: normalizeDisplayName(item.name),
|
||||||
download_path: item.download_path || `/api/transfers/${encodeURIComponent(item.transfer_id)}/fallback/download`,
|
download_path: item.download_path || `/api/transfers/${encodeURIComponent(item.transfer_id)}/fallback/download`,
|
||||||
size_label: formatFileSize(Number(item.size_bytes || 0)),
|
size_label: formatFileSize(Number(item.size_bytes || 0)),
|
||||||
created_label: formatRelativeTime(item.created_at),
|
created_label: formatRelativeTime(item.created_at),
|
||||||
@@ -551,7 +552,7 @@ function addFiles(files) {
|
|||||||
id: createId(`file-${index}`),
|
id: createId(`file-${index}`),
|
||||||
kind: 'file',
|
kind: 'file',
|
||||||
file,
|
file,
|
||||||
name: file.name,
|
name: normalizeDisplayName(file.name),
|
||||||
size: formatFileSize(file.size),
|
size: formatFileSize(file.size),
|
||||||
sizeBytes: file.size,
|
sizeBytes: file.size,
|
||||||
status: '待发送',
|
status: '待发送',
|
||||||
@@ -1166,15 +1167,47 @@ function ensureDeviceId() {
|
|||||||
|
|
||||||
function ensureDeviceName(deviceId) {
|
function ensureDeviceName(deviceId) {
|
||||||
let deviceName = localStorage.getItem(DEVICE_NAME_KEY)
|
let deviceName = localStorage.getItem(DEVICE_NAME_KEY)
|
||||||
if (!deviceName) {
|
if (!deviceName || isLegacyAutoDeviceName(deviceName, deviceId)) {
|
||||||
const platform = navigator.userAgentData?.platform || navigator.platform || 'Web'
|
deviceName = `${detectDeviceLabel()} ${deviceId.slice(0, 4)}`
|
||||||
deviceName = `${platform} ${deviceId.slice(0, 4)}`
|
|
||||||
localStorage.setItem(DEVICE_NAME_KEY, deviceName)
|
localStorage.setItem(DEVICE_NAME_KEY, deviceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
return deviceName
|
return deviceName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isLegacyAutoDeviceName(deviceName, deviceId) {
|
||||||
|
const value = String(deviceName || '').trim()
|
||||||
|
const suffix = deviceId.slice(0, 4)
|
||||||
|
if (!value || !suffix || !value.endsWith(` ${suffix}`)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return /^(android|iphone|ipad|linux|macintel|macos|windows|win32|web)\s/i.test(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectDeviceLabel() {
|
||||||
|
const source = `${navigator.userAgent} ${navigator.platform}`.toLowerCase()
|
||||||
|
if (source.includes('iphone')) {
|
||||||
|
return 'iPhone'
|
||||||
|
}
|
||||||
|
if (source.includes('ipad')) {
|
||||||
|
return 'iPad'
|
||||||
|
}
|
||||||
|
if (source.includes('android')) {
|
||||||
|
return 'Android'
|
||||||
|
}
|
||||||
|
if (source.includes('windows') || source.includes('win32')) {
|
||||||
|
return 'Windows'
|
||||||
|
}
|
||||||
|
if (source.includes('mac os') || source.includes('macintosh') || source.includes('macintel')) {
|
||||||
|
return 'macOS'
|
||||||
|
}
|
||||||
|
if (source.includes('linux')) {
|
||||||
|
return 'Linux'
|
||||||
|
}
|
||||||
|
return 'Web'
|
||||||
|
}
|
||||||
|
|
||||||
function detectDeviceType() {
|
function detectDeviceType() {
|
||||||
const source = `${navigator.userAgent} ${navigator.platform}`.toLowerCase()
|
const source = `${navigator.userAgent} ${navigator.platform}`.toLowerCase()
|
||||||
if (source.includes('iphone') || source.includes('android') || source.includes('mobile')) {
|
if (source.includes('iphone') || source.includes('android') || source.includes('mobile')) {
|
||||||
@@ -1186,6 +1219,23 @@ function detectDeviceType() {
|
|||||||
return 'desktop'
|
return 'desktop'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeDisplayName(value, fallback = 'file') {
|
||||||
|
const raw = String(value || '').trim()
|
||||||
|
if (!raw) {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/%[0-9A-Fa-f]{2}/.test(raw)) {
|
||||||
|
return raw
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(raw)
|
||||||
|
} catch {
|
||||||
|
return raw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function mapDeviceIcon(type) {
|
function mapDeviceIcon(type) {
|
||||||
if (type === 'phone') {
|
if (type === 'phone') {
|
||||||
return 'smartphone'
|
return 'smartphone'
|
||||||
@@ -1600,7 +1650,7 @@ function handleIncomingRealtimeFileMeta(payload) {
|
|||||||
ensurePeerSession(sender, true)
|
ensurePeerSession(sender, true)
|
||||||
|
|
||||||
incomingFileBuffers.set(payload.transfer_id, {
|
incomingFileBuffers.set(payload.transfer_id, {
|
||||||
name: payload.name || 'file',
|
name: normalizeDisplayName(payload.name, 'file'),
|
||||||
mimeType: payload.mime_type || 'application/octet-stream',
|
mimeType: payload.mime_type || 'application/octet-stream',
|
||||||
sizeBytes: Number(payload.size_bytes || 0),
|
sizeBytes: Number(payload.size_bytes || 0),
|
||||||
receivedBytes: 0,
|
receivedBytes: 0,
|
||||||
@@ -1613,7 +1663,7 @@ function handleIncomingRealtimeFileMeta(payload) {
|
|||||||
id: createId('incoming-file'),
|
id: createId('incoming-file'),
|
||||||
transferId: payload.transfer_id,
|
transferId: payload.transfer_id,
|
||||||
kind: 'file',
|
kind: 'file',
|
||||||
name: payload.name || 'file',
|
name: normalizeDisplayName(payload.name, 'file'),
|
||||||
size: formatFileSize(Number(payload.size_bytes || 0)),
|
size: formatFileSize(Number(payload.size_bytes || 0)),
|
||||||
sizeBytes: Number(payload.size_bytes || 0),
|
sizeBytes: Number(payload.size_bytes || 0),
|
||||||
status: '正在接收...',
|
status: '正在接收...',
|
||||||
@@ -1965,7 +2015,7 @@ function handleIncomingTransferCreated(envelope) {
|
|||||||
id: createId('incoming-file'),
|
id: createId('incoming-file'),
|
||||||
transferId: payload.transfer_id,
|
transferId: payload.transfer_id,
|
||||||
kind: 'file',
|
kind: 'file',
|
||||||
name: payload.name || 'file',
|
name: normalizeDisplayName(payload.name, 'file'),
|
||||||
size: formatFileSize(Number(payload.size_bytes || 0)),
|
size: formatFileSize(Number(payload.size_bytes || 0)),
|
||||||
sizeBytes: Number(payload.size_bytes || 0),
|
sizeBytes: Number(payload.size_bytes || 0),
|
||||||
status: '接收中...',
|
status: '接收中...',
|
||||||
@@ -2010,7 +2060,7 @@ function handleIncomingTransferFile(envelope) {
|
|||||||
id: createId('incoming-file'),
|
id: createId('incoming-file'),
|
||||||
transferId: payload.transfer_id,
|
transferId: payload.transfer_id,
|
||||||
kind: 'file',
|
kind: 'file',
|
||||||
name: payload.name || 'file',
|
name: normalizeDisplayName(payload.name, 'file'),
|
||||||
size: '',
|
size: '',
|
||||||
sizeBytes: 0,
|
sizeBytes: 0,
|
||||||
status: '可保存',
|
status: '可保存',
|
||||||
@@ -2742,7 +2792,7 @@ handleIncomingTransferCreated = function handleIncomingTransferCreatedOverride(e
|
|||||||
id: createId('incoming-file'),
|
id: createId('incoming-file'),
|
||||||
transferId: payload.transfer_id,
|
transferId: payload.transfer_id,
|
||||||
kind: 'file',
|
kind: 'file',
|
||||||
name: payload.name || 'file',
|
name: normalizeDisplayName(payload.name, 'file'),
|
||||||
size: formatFileSize(Number(payload.size_bytes || 0)),
|
size: formatFileSize(Number(payload.size_bytes || 0)),
|
||||||
sizeBytes: Number(payload.size_bytes || 0),
|
sizeBytes: Number(payload.size_bytes || 0),
|
||||||
status: '等待接收...',
|
status: '等待接收...',
|
||||||
@@ -2791,7 +2841,7 @@ handleIncomingTransferFile = function handleIncomingTransferFileOverride(envelop
|
|||||||
id: createId('incoming-file'),
|
id: createId('incoming-file'),
|
||||||
transferId: payload.transfer_id,
|
transferId: payload.transfer_id,
|
||||||
kind: 'file',
|
kind: 'file',
|
||||||
name: payload.name || 'file',
|
name: normalizeDisplayName(payload.name, 'file'),
|
||||||
size: '',
|
size: '',
|
||||||
sizeBytes: 0,
|
sizeBytes: 0,
|
||||||
status: '可保存',
|
status: '可保存',
|
||||||
|
|||||||
Reference in New Issue
Block a user