Axios同一个接口接收文件或json

需求背景介绍

文件转换功能, 如上传jpg转为png, 服务器可能会返回正常的png文件, 也可能会返回{code: 5001, msg: "读取文件失败, 请检查文件完整性"}

或者Excel导入功能, 服务器可能返回{code: 2000, msg: “文件导入成功”}, 也可能返回一个带有错误信息的文件

此时后台(示例为springboot)文件处理机制大致流程:

@PostMapping("jpg2png")
public void jpg2png(MultipartFile file, HttpServletResponse resp) throws IOException {
    // 假设此时为正常返回, 但是抛出异常被全局异常捕获, 返回了json
    IOUtils.copy(convert(file), resp.getOutputStream());
}

public InputStream convert(MultipartFile file) {
    // 模拟可能发生的异常
    throw new RuntimeException("文件转换失败");
}

那么在axios实例中调用

const jpg2png = (file: File)=>{
  const fd = new FormData()
  fd.append('file', file)
  return axios.post('http://xxx:xx/jpg2png', fd, {responseType: 'blob'})
}

此时jpg2png返回格式只有blog, 并非我们想要的json或blob

解决方案

方案其实很简单, 在响应拦截器中判断, 如果返回内容为blob, 但是响应头中的content-type值却是’application/json’(如果成功的话会是对应的文件类型, 但后台有特殊情况的请自行调整)

放一下我在用的拦截器

import axios, {type AxiosInstance} from 'axios'
import {FileUtil} from '@/util/file-util'

const instance = axios.create({
  timeout: 5 * 1000,
  baseURL: import.meta.env.VITE_API_BASE_URL
})

instance.interceptors.request.use(config => {
  config.headers.token = localStorage.getItem('token')
  return config
}, error => {
  console.error('req error : ', error)
  if (axios.isCancel(error)) {
    return error
  }
  return Promise.reject(error)
})

instance.interceptors.response.use(async response => {
  // 请求时设置返回blob, 但是实际上可能返回的是json的情况
  if (response.data instanceof Blob) {
    if (!response.headers['content-type']?.includes('application/json')) {
      FileUtil.saveBlob(response.data, decodeURI(response.headers['filename'] ?? ''))
      return null
    }
    response.data = JSON.parse(await (response.data as Blob).text())
  }
  const {
    code,
    message,
    data
  } = response.data
  if (code !== 2000) {
    alert(message)
    return Promise.reject(response)
  }
  return data
}, error => {
  alert('请求异常')
  console.error('请求异常: ', error)
  return Promise.reject(error)
})

export const rest: AxiosInstance = instance

再来一个js版本的:

import axios from 'axios'
import {FileUtil} from '@/util/file-util'

const instance = axios.create({
  timeout: 5 * 1000,
  baseURL: import.meta.env.VITE_API_BASE_URL
})

instance.interceptors.request.use(config => {
  config.headers.token = localStorage.getItem('token')
  return config
}, error => {
  console.error('req error : ', error)
  if (axios.isCancel(error)) {
    return error
  }
  return Promise.reject(error)
})

instance.interceptors.response.use(async response => {
  // 请求时设置返回blob, 但是实际上可能返回的是json的情况
  if (response.data instanceof Blob) {
    if (!response.headers['content-type']?.includes('application/json')) {
      FileUtil.saveBlob(response.data, decodeURI(response.headers['filename'] ?? ''))
      return null
    }
    response.data = JSON.parse(await response.data.text())
  }
  const {
    code,
    message,
    data
  } = response.data
  if (code !== 2000) {
    alert(message)
    return Promise.reject(response)
  }
  return data
}, error => {
  alert('请求异常')
  console.error('请求异常: ', error)
  return Promise.reject(error)
})

export const rest = instance

核心逻辑就是if (response.data instanceof Blob) {这段代码了

附:

file-util.ts

export class FileUtil {
  static #tagA = document.createElement('a')

  /**
   * 保存文件
   */
  static saveBlob(file: Blob, filename: string) {
    this.#tagA.download = filename
    const url = URL.createObjectURL(file)
    this.#tagA.href = url
    this.#tagA.click()
    URL.revokeObjectURL(url)
  }
}

总结

没什么优雅的方案, 我的需求仅仅是拦截器拦截到文件则自动下载即可, 业务代码无需后续处理, 如果是请求文件, 返回null, 表示处理完成.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Vue项目中创建接口用来接受axios库发送的POST请求,需要进行以下步骤: 1. 在Vue项目中安装axios库。可以使用npm命令进行安装:`npm install axios --save`。 2. 在Vue项目的main.js文件中引入axios库,并挂载到Vue实例上: ``` import axios from 'axios' Vue.prototype.$http = axios ``` 这样就可以在Vue组件中使用`this.$http`来发送HTTP请求。 3. 在Vue组件中定义一个方法,用来发送POST请求。可以使用axios库的`post`方法来发送POST请求,并在请求中传递数据。下面是一个示例代码: ``` methods: { postData() { const data = { name: 'John', age: 30 } this.$http.post('/api/data', data) .then(response => { console.log(response.data) }) .catch(error => { console.log(error) }) } } ``` 在上面的代码中,使用`this.$http.post`来发送POST请求,请求的URL为`/api/data`,请求的数据为一个包含名字和年龄的对象。 4. 在后端代码中,创建一个路由,用来接受POST请求。具体的创建方式,可以参考前面的回答,这里不再赘述。 5. 在路由处理函数中,使用req.body来获取POST请求中的数据,并返回相应的结果。以下是一个使用Node.js和Express框架创建接口的示例代码: ``` const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(bodyParser.json()); app.post('/api/data', (req, res) => { const data = req.body; // 处理POST请求中的数据 res.send('接收到POST请求'); }); app.listen(3000, () => { console.log('服务器已启动'); }); ``` 在上面的代码中,使用body-parser中间件来解析请求体,使用`req.body`来获取POST请求中的数据,并返回一个字符串"接收到POST请求"。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值