75 行
1.9 KiB
TypeScript
75 行
1.9 KiB
TypeScript
|
|
import { getConfig, _getToken } from '@xuqm/rn-common'
|
||
|
|
|
||
|
|
export interface UploadResult {
|
||
|
|
url: string
|
||
|
|
thumbnailUrl: string | null
|
||
|
|
hash: string
|
||
|
|
size: number
|
||
|
|
originalName: string
|
||
|
|
mimeType: string
|
||
|
|
ext?: string
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Upload a file to the file-service using multipart/form-data.
|
||
|
|
* Optionally attach a pre-generated thumbnail (for video files).
|
||
|
|
* Requires an active IM session (Bearer token) for authentication.
|
||
|
|
*/
|
||
|
|
export async function uploadFile(
|
||
|
|
localUri: string,
|
||
|
|
mimeType: string,
|
||
|
|
filename: string,
|
||
|
|
thumbnailUri?: string,
|
||
|
|
): Promise<UploadResult> {
|
||
|
|
const config = getConfig()
|
||
|
|
const token = await _getToken()
|
||
|
|
if (!token) {
|
||
|
|
throw new Error('[uploadFile] No active session — call ImSDK.login() first.')
|
||
|
|
}
|
||
|
|
|
||
|
|
const form = new FormData()
|
||
|
|
|
||
|
|
// React Native FormData accepts an object with uri/name/type for file parts
|
||
|
|
form.append('file', {
|
||
|
|
uri: localUri,
|
||
|
|
name: filename,
|
||
|
|
type: mimeType,
|
||
|
|
} as unknown as Blob)
|
||
|
|
|
||
|
|
if (thumbnailUri) {
|
||
|
|
form.append('thumbnail', {
|
||
|
|
uri: thumbnailUri,
|
||
|
|
name: 'thumbnail.jpg',
|
||
|
|
type: 'image/jpeg',
|
||
|
|
} as unknown as Blob)
|
||
|
|
}
|
||
|
|
|
||
|
|
const response = await fetch(`${config.fileServiceUrl}/api/file/upload`, {
|
||
|
|
method: 'POST',
|
||
|
|
headers: {
|
||
|
|
Authorization: `Bearer ${token}`,
|
||
|
|
// Do NOT set Content-Type — let fetch set it with the correct boundary
|
||
|
|
},
|
||
|
|
body: form,
|
||
|
|
})
|
||
|
|
|
||
|
|
if (!response.ok) {
|
||
|
|
const text = await response.text().catch(() => '')
|
||
|
|
throw new Error(`[uploadFile] Upload failed (${response.status}): ${text}`)
|
||
|
|
}
|
||
|
|
|
||
|
|
const json = await response.json()
|
||
|
|
|
||
|
|
// Server wraps result in ApiResponse { code, data, message }
|
||
|
|
const data = json?.data ?? json
|
||
|
|
return {
|
||
|
|
url: data.url,
|
||
|
|
thumbnailUrl: data.thumbnailUrl ?? null,
|
||
|
|
hash: data.hash,
|
||
|
|
size: data.size,
|
||
|
|
originalName: data.originalName,
|
||
|
|
mimeType: data.mimeType,
|
||
|
|
ext: data.ext,
|
||
|
|
}
|
||
|
|
}
|