Browse Source
fix: request download and upload not support `responseReturn` (#5456)
* fix: request download and upload not support `responseReturn`
* docs: update
* fix: type of request client upload result
pull/5465/head
Netfan
1 year ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with
93 additions and
28 deletions
-
packages/effects/request/src/request-client/modules/downloader.test.ts
-
packages/effects/request/src/request-client/modules/downloader.ts
-
packages/effects/request/src/request-client/modules/uploader.ts
-
packages/effects/request/src/request-client/preset-interceptors.ts
-
packages/effects/request/src/request-client/types.ts
-
playground/src/api/examples/download.ts
-
playground/src/views/demos/features/file-download/index.vue
|
|
|
@ -31,6 +31,7 @@ describe('fileDownloader', () => { |
|
|
|
expect(result).toEqual(mockBlob); |
|
|
|
expect(mockAxiosInstance.get).toHaveBeenCalledWith(url, { |
|
|
|
responseType: 'blob', |
|
|
|
responseReturn: 'body', |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
@ -51,6 +52,7 @@ describe('fileDownloader', () => { |
|
|
|
expect(mockAxiosInstance.get).toHaveBeenCalledWith(url, { |
|
|
|
...customConfig, |
|
|
|
responseType: 'blob', |
|
|
|
responseReturn: 'body', |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
@ -1,7 +1,14 @@ |
|
|
|
import type { AxiosRequestConfig } from 'axios'; |
|
|
|
|
|
|
|
import type { RequestClient } from '../request-client'; |
|
|
|
import type { RequestResponse } from '../types'; |
|
|
|
import type { RequestClientConfig } from '../types'; |
|
|
|
|
|
|
|
type DownloadRequestConfig = { |
|
|
|
/** |
|
|
|
* 定义期望获得的数据类型。 |
|
|
|
* raw: 原始的AxiosResponse,包括headers、status等。 |
|
|
|
* body: 只返回响应数据的BODY部分(Blob) |
|
|
|
*/ |
|
|
|
responseReturn?: 'body' | 'raw'; |
|
|
|
} & Omit<RequestClientConfig, 'responseReturn'>; |
|
|
|
|
|
|
|
class FileDownloader { |
|
|
|
private client: RequestClient; |
|
|
|
@ -9,20 +16,23 @@ class FileDownloader { |
|
|
|
constructor(client: RequestClient) { |
|
|
|
this.client = client; |
|
|
|
} |
|
|
|
|
|
|
|
public async download( |
|
|
|
/** |
|
|
|
* 下载文件 |
|
|
|
* @param url 文件的完整链接 |
|
|
|
* @param config 配置信息,可选。 |
|
|
|
* @returns 如果config.responseReturn为'body',则返回Blob(默认),否则返回RequestResponse<Blob> |
|
|
|
*/ |
|
|
|
public async download<T = Blob>( |
|
|
|
url: string, |
|
|
|
config?: AxiosRequestConfig, |
|
|
|
): Promise<RequestResponse<Blob>> { |
|
|
|
const finalConfig: AxiosRequestConfig = { |
|
|
|
config?: DownloadRequestConfig, |
|
|
|
): Promise<T> { |
|
|
|
const finalConfig: DownloadRequestConfig = { |
|
|
|
responseReturn: 'body', |
|
|
|
...config, |
|
|
|
responseType: 'blob', |
|
|
|
}; |
|
|
|
|
|
|
|
const response = await this.client.get<RequestResponse<Blob>>( |
|
|
|
url, |
|
|
|
finalConfig, |
|
|
|
); |
|
|
|
const response = await this.client.get<T>(url, finalConfig); |
|
|
|
|
|
|
|
return response; |
|
|
|
} |
|
|
|
|
|
|
|
@ -1,6 +1,5 @@ |
|
|
|
import type { AxiosRequestConfig, AxiosResponse } from 'axios'; |
|
|
|
|
|
|
|
import type { RequestClient } from '../request-client'; |
|
|
|
import type { RequestClientConfig } from '../types'; |
|
|
|
|
|
|
|
class FileUploader { |
|
|
|
private client: RequestClient; |
|
|
|
@ -9,18 +8,18 @@ class FileUploader { |
|
|
|
this.client = client; |
|
|
|
} |
|
|
|
|
|
|
|
public async upload( |
|
|
|
public async upload<T = any>( |
|
|
|
url: string, |
|
|
|
data: Record<string, any> & { file: Blob | File }, |
|
|
|
config?: AxiosRequestConfig, |
|
|
|
): Promise<AxiosResponse> { |
|
|
|
config?: RequestClientConfig, |
|
|
|
): Promise<T> { |
|
|
|
const formData = new FormData(); |
|
|
|
|
|
|
|
Object.entries(data).forEach(([key, value]) => { |
|
|
|
formData.append(key, value); |
|
|
|
}); |
|
|
|
|
|
|
|
const finalConfig: AxiosRequestConfig = { |
|
|
|
const finalConfig: RequestClientConfig = { |
|
|
|
...config, |
|
|
|
headers: { |
|
|
|
'Content-Type': 'multipart/form-data', |
|
|
|
|
|
|
|
@ -25,15 +25,15 @@ export const defaultResponseInterceptor = ({ |
|
|
|
if (config.responseReturn === 'raw') { |
|
|
|
return response; |
|
|
|
} |
|
|
|
const code = responseData[codeField]; |
|
|
|
if ( |
|
|
|
status >= 200 && status < 400 && isFunction(successCode) |
|
|
|
? successCode(code) |
|
|
|
: code === successCode |
|
|
|
) { |
|
|
|
|
|
|
|
if (status >= 200 && status < 400) { |
|
|
|
if (config.responseReturn === 'body') { |
|
|
|
return responseData; |
|
|
|
} else { |
|
|
|
} else if ( |
|
|
|
isFunction(successCode) |
|
|
|
? successCode(responseData[codeField]) |
|
|
|
: responseData[codeField] === successCode |
|
|
|
) { |
|
|
|
return isFunction(dataField) |
|
|
|
? dataField(responseData) |
|
|
|
: responseData[dataField]; |
|
|
|
|
|
|
|
@ -7,9 +7,9 @@ import type { |
|
|
|
|
|
|
|
type ExtendOptions = { |
|
|
|
/** 响应数据的返回方式。 |
|
|
|
* raw: 原始的AxiosResponse,包括headers、status等。 |
|
|
|
* body: 返回响应数据的BODY部分。 |
|
|
|
* data: 解构响应的BODY数据,只返回其中的data节点数据。 |
|
|
|
* raw: 原始的AxiosResponse,包括headers、status等,不做是否成功请求的检查。 |
|
|
|
* body: 返回响应数据的BODY部分(只会根据status检查请求是否成功,忽略对code的判断,这种情况下应由调用方检查请求是否成功)。 |
|
|
|
* data: 解构响应的BODY数据,只返回其中的data节点数据(会检查status和code是否为成功状态)。 |
|
|
|
*/ |
|
|
|
responseReturn?: 'body' | 'data' | 'raw'; |
|
|
|
}; |
|
|
|
|
|
|
|
@ -0,0 +1,28 @@ |
|
|
|
import type { RequestResponse } from '@vben/request'; |
|
|
|
|
|
|
|
import { requestClient } from '../request'; |
|
|
|
|
|
|
|
/** |
|
|
|
* 下载文件,获取Blob |
|
|
|
* @returns Blob |
|
|
|
*/ |
|
|
|
async function downloadFile1() { |
|
|
|
return requestClient.download<Blob>( |
|
|
|
'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp', |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 下载文件,获取完整的Response |
|
|
|
* @returns RequestResponse<Blob> |
|
|
|
*/ |
|
|
|
async function downloadFile2() { |
|
|
|
return requestClient.download<RequestResponse<Blob>>( |
|
|
|
'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp', |
|
|
|
{ |
|
|
|
responseReturn: 'raw', |
|
|
|
}, |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
export { downloadFile1, downloadFile2 }; |
|
|
|
@ -1,4 +1,6 @@ |
|
|
|
<script setup lang="ts"> |
|
|
|
import { ref } from 'vue'; |
|
|
|
|
|
|
|
import { Page } from '@vben/common-ui'; |
|
|
|
import { |
|
|
|
downloadFileFromBase64, |
|
|
|
@ -9,7 +11,23 @@ import { |
|
|
|
|
|
|
|
import { Button, Card } from 'ant-design-vue'; |
|
|
|
|
|
|
|
import { downloadFile1, downloadFile2 } from '#/api/examples/download'; |
|
|
|
|
|
|
|
import imageBase64 from './base64'; |
|
|
|
|
|
|
|
const downloadResult = ref(''); |
|
|
|
|
|
|
|
function getBlob() { |
|
|
|
downloadFile1().then((res) => { |
|
|
|
downloadResult.value = `获取Blob成功,长度:${res.size}`; |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
function getResponse() { |
|
|
|
downloadFile2().then((res) => { |
|
|
|
downloadResult.value = `获取Response成功,headers:${JSON.stringify(res.headers)},长度:${res.data.size}`; |
|
|
|
}); |
|
|
|
} |
|
|
|
</script> |
|
|
|
|
|
|
|
<template> |
|
|
|
@ -70,5 +88,13 @@ import imageBase64 from './base64'; |
|
|
|
Download TxT |
|
|
|
</Button> |
|
|
|
</Card> |
|
|
|
|
|
|
|
<Card class="my-5" title="Request download"> |
|
|
|
<Button type="primary" @click="getBlob"> 获取Blob </Button> |
|
|
|
<Button type="primary" class="ml-4" @click="getResponse"> |
|
|
|
获取Response |
|
|
|
</Button> |
|
|
|
<div class="mt-4">{{ downloadResult }}</div> |
|
|
|
</Card> |
|
|
|
</Page> |
|
|
|
</template> |
|
|
|
|