文件下载

master
lzq 2025-12-20 16:12:17 +08:00
parent fab8e1d9ad
commit 4b65fb8d5f
4 changed files with 66 additions and 94 deletions

View File

@ -1,9 +1,10 @@
import {
download,
get,
getFileUrl,
} from '@/common/utils/http-util.ts'
import { saveFile } from '@/common/app'
interface PresignedUrl extends Record<string, string | undefined> {
url?: string
bucketName?: string
@ -37,7 +38,7 @@ export default {
obtainPresignedUrl(filename: string) {
return get<PresignedUrl>('/oss/obtain_presigned_url', {filename})
},
download(filename: string, params?: any, defaultName: string = '下载的文件') {
ossDownload(filename: string, params?: any) {
let url = ''
if (filename.startsWith('http') || filename.startsWith('https')) {
url = filename
@ -47,26 +48,7 @@ export default {
} else {
url = '/oss/download/' + filename
}
return download(url, params, defaultName)
.then(res => {
// 创建新的URL并指向File对象或者Blob对象的地址
const blobURL = window.URL.createObjectURL(res.data.data)
// 创建a标签用于跳转至下载链接
const tempLink = document.createElement('a')
tempLink.style.display = 'none'
tempLink.href = blobURL
tempLink.setAttribute('download', decodeURI(res.data.filename))
// 兼容某些浏览器不支持HTML5的download属性
if (typeof tempLink.download === 'undefined') {
tempLink.setAttribute('target', '_blank')
}
// 挂载a标签
document.body.appendChild(tempLink)
tempLink.click()
document.body.removeChild(tempLink)
// 释放blob URL地址
window.URL.revokeObjectURL(blobURL)
})
saveFile(get<{ content: Blob, filename: string }>(url, params, {responseType: 'arraybuffer'}))
},
fileUrl(filename?: string) {
if (filename == null || filename.length <= 0) {

View File

@ -3,6 +3,7 @@ import Colls from '@/common/utils/colls.ts'
import { useAppSettingStore } from '@/common/app/app-setting-store.ts'
import { useAppUserStore } from '@/common/app/app-user-store.ts'
import Utils from '@/common/utils'
import type { R } from '@/common/utils/http-util.ts'
export const reloadUserInfo = () => {
const appSettingStore = useAppSettingStore()
@ -64,3 +65,27 @@ export function isAdmin() {
const appUserStore = useAppUserStore()
return appUserStore.roles != null && appUserStore.roles.includes('ROLE_ADMIN')
}
export function saveFile(r: Promise<R<{ content: Blob, filename: string }>>, defaultName: string = '下载的文件') {
return r.then(res => {
const blobURL = window.URL.createObjectURL(res.data.content)
const tempLink = document.createElement('a')
try {
tempLink.style.display = 'none'
tempLink.href = blobURL
tempLink.setAttribute('download', decodeURI(res.data.filename ?? defaultName))
if (typeof tempLink.download === 'undefined') {
tempLink.setAttribute('target', '_blank')
}
document.body.appendChild(tempLink)
tempLink.click()
return Promise.resolve()
} catch (e) {
console.log('下载失败', e)
return Promise.reject()
} finally {
if (tempLink != null) document.body.removeChild(tempLink)
if (blobURL != null) window.URL.revokeObjectURL(blobURL)
}
})
}

View File

@ -22,9 +22,7 @@ export interface R<T = any> {
headers?: AxiosRequestConfig['headers']
}
type AxiosConfig = Pick<AxiosRequestConfig, 'headers' | 'params' | 'responseType'>
// const closeUrls = closeUrl.split(',')
// type ParamsSerializerType = Extract<AxiosRequestConfig['paramsSerializer'], any>;
type AxiosConfig = Pick<AxiosRequestConfig, 'headers' | 'params' | 'responseType' | 'paramsSerializer'>
/**
* Query
@ -38,13 +36,11 @@ const paramsSerializer = (params: any) => {
/**
*
*/
/* function errHandler(r?: R) {
Toast.error(r?.message ?? '操作失败')
} */
const errHandler = Utils.throttle(500, (r?: AxiosResponse<R<any>, any>) => {
console.log('异常处理', r)
ElMessage.error(r?.data?.message ?? '服务器错误')
ElMessage.error(r?.data?.message ?? (r?.data?.msg ?? '服务器错误'))
})
/**
* axios
*/
@ -78,20 +74,23 @@ httpUtil.interceptors.request.use(
*/
httpUtil.interceptors.response.use(
response => {
// console.log('HTTP 请求结果', response.config.url, response)
// vite 代理失败时 响应码为 200 响应内容为空
if (response.config.responseType !== 'json') {
return Promise.resolve(response)
}
if (response.data == null) {
response.data = {code: 0, success: true, msg: '无响应内容', message: '无响应内容', data: null, headers: response.headers}
response.data = {code: 5555, success: false, msg: '无响应内容', message: '无响应内容', data: null}
} else if (response.config.responseType === 'arraybuffer') {
const data = response.data as ArrayBufferView
if (data.byteLength <= 0) {
response.data = {code: 9999, success: false, msg: '文件获取失败', message: '文件获取失败', data: null}
} else {
const contentDisposition = response.headers['Content-Disposition'] ?? response.headers['content-disposition']
const filename = getFileName(contentDisposition)
// 将二进制流转为blob
const blob = new Blob([ data ])
response.data = {code: 0, success: true, msg: '成功', message: '文件获取成功', data: {content: blob, filename}}
}
}
response.data.headers = response.headers
if (response.data.code === 0) {
return Promise.resolve(response)
} else {
return Promise.reject(response)
}
return response.data.code === 0 ? Promise.resolve(response) : Promise.reject(response)
},
error => {
console.error('HTTP 请求失败', error)
@ -114,17 +113,21 @@ httpUtil.interceptors.response.use(
)
/**
* GET JSON
* GET
*
* @param url
* @param params Query
* @param config
* @param disposeErr -->true
*/
export function get<T = any>(url: string, params?: any, disposeErr: boolean = true) {
/* if (closeUrls.includes(url)) {
return Promise.reject({code: 0, success: true, msg: '', message: '', data: null} as R<T>)
} */
return httpUtil.get<R<T>>(url, {params, paramsSerializer, responseType: 'json'})
export function get<T = any>(url: string, params?: any, config: AxiosConfig = {}, disposeErr: boolean = true) {
if (config.responseType == null) {
config.responseType = 'json'
}
config.params = params
config.paramsSerializer = paramsSerializer
return httpUtil.get<R<T>>(url, config)
.then(({data}) => data)
.catch(res => {
if (disposeErr) errHandler(res)
@ -133,18 +136,21 @@ export function get<T = any>(url: string, params?: any, disposeErr: boolean = tr
}
/**
* POST JSON
* POST
*
* @param url
* @param body Body
* @param params
* @param config
* @param disposeErr -->true
*/
export function post<T>(url: string, body?: any, params?: any, disposeErr: boolean = true) {
/* if (closeUrls.includes(url)) {
return Promise.reject({code: 0, success: true, msg: '', message: '', data: null} as R<T>)
} */
return httpUtil.post<R<T>>(url, body, {responseType: 'json', params})
export function post<T>(url: string, body?: any, params?: any, config: AxiosConfig = {}, disposeErr: boolean = true) {
if (config.responseType == null) {
config.responseType = 'json'
}
config.params = params
config.paramsSerializer = paramsSerializer
return httpUtil.post<R<T>>(url, body, config)
.then(({data}) => data)
.catch(res => {
if (disposeErr) errHandler(res)
@ -222,26 +228,6 @@ function getFileName(contentDisposition: string) {
return decodeURIComponent(fileNameWithoutQuotes)
}
export function download(url: string, data?: any, params?: any, defaultName: string = '下载的文件', disposeErr: boolean = true) {
return httpUtil.post(url, data, {params, paramsSerializer, responseType: 'arraybuffer'})
.then(res => {
const data = res.data
if (!data || data.byteLength <= 0) {
// 错误提示
return Promise.reject({code: 9999, success: false, msg: '文件获取失败', message: '文件获取失败', data: null, headers: res.headers})
}
const contentDisposition = res.headers['Content-Disposition'] ?? res.headers['content-disposition']
const filename = getFileName(contentDisposition) ?? defaultName
// 将二进制流转为blob
const blob = new Blob([ data ])
return Promise.resolve({code: 0, success: true, msg: '成功', message: '文件获取成功', data: {data: blob, filename}, headers: res.headers})
})
.catch(res => {
if (disposeErr) errHandler(res)
return Promise.reject(res)
})
}
export function getFileUrl(path?: string) {
if (path == null || path.length <= 0) {
return ''
@ -255,7 +241,6 @@ export default {
post,
postForm,
postMltForm,
download,
getFileUrl,
ins: httpUtil,
}

View File

@ -1,8 +1,8 @@
import {
download,
get,
post,
} from '@/common/utils/http-util.ts'
import { saveFile } from '@/common/app'
export default {
paging(data: TplTypes.SearchTplParam) {
@ -23,30 +23,10 @@ export default {
listAll(lang: string) {
return get<TplTypes.SearchTplResult[]>('/tpl/list_all', {lang})
},
preview(lang: string, tableName: string, data: Record<string, any>) {
return post<TplTypes.CodeInfo>('/tpl/preview', data, {lang, tableName})
},
download(lang: string, tableName: string, data: Record<string, any>) {
return download('/tpl/download', data, {lang, tableName})
.then(res => {
// 创建新的URL并指向File对象或者Blob对象的地址
const blobURL = window.URL.createObjectURL(res.data.data)
// 创建a标签用于跳转至下载链接
const tempLink = document.createElement('a')
tempLink.style.display = 'none'
tempLink.href = blobURL
tempLink.setAttribute('download', decodeURI(res.data.filename))
// 兼容某些浏览器不支持HTML5的download属性
if (typeof tempLink.download === 'undefined') {
tempLink.setAttribute('target', '_blank')
}
// 挂载a标签
document.body.appendChild(tempLink)
tempLink.click()
document.body.removeChild(tempLink)
// 释放blob URL地址
window.URL.revokeObjectURL(blobURL)
})
return saveFile(post('/tpl/download', data, {lang, tableName}, {responseType: 'arraybuffer'}))
},
}