文件上传与下载(一)

一、前端:

1、request请求util:

import axios from 'axios'
// 配置session跨域
axios.defaults.withCredentials = true
import { Message, MessageBox } from 'element-ui'
import store from '../store'
import { getToken } from '@/utils/auth'

// 创建axios实例
const service = axios.create({
  baseURL: process.env.BASE_API, // api的base_url
  timeout: 5000 // 请求超时时间
})

// request拦截器
service.interceptors.request.use(config => {
  if (store.getters.token) {
    config.headers['token'] = store.getters.token // 让每个请求携带自定义token 请根据实际情况自行修改
  }
  //文件上传
  if (config.method === 'post') {
    if (config.upload) { // 上传图片则添加headers
      config.headers['Content-Type'] = 'multipart/form-data'
      config.timeout = 360000
    } 
  }
  return config
}, error => {
  // Do something with request error
  console.log(error) // for debug
  Promise.reject(error)
})

// respone拦截器
service.interceptors.response.use(
  response => {
  /**
  * code为非20000是抛错 可结合自己业务进行修改
  */
    const res = response.data
    if (res.code !== 200 && res.code !=300) {
      Message({
        message: res.message,
        type: 'error',
        duration: 5 * 1000
      })

      // 401 token失效
      if (res.code === 401) {
        // MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
        //   confirmButtonText: '重新登录',
        //   cancelButtonText: '取消',
        //   type: 'warning'
        // }).then(() => {
        //   store.dispatch('FedLogOut').then(() => {
        //     location.reload()// 为了重新实例化vue-router对象 避免bug
        //   })
        // })
        store.dispatch('FedLogOut').then(() => {
          location.reload()// 为了重新实例化vue-router对象 避免bug
        })
      }
      return Promise.reject('error')
    } else {
      return response.data
    }
  },
  error => {
    console.log('err' + error)// for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export default service

2、api:

import request from '@/utils/request'

export function upload(formDatas) {
  return request({
    url: '/upload/upload',
    method: 'post',
    upload: true,
    data: formDatas
  })
}

3、页面:

(1)单独文件上传

<template>
  <div>
    <div>
    
      <!-- <a href="javacript:void(0)" @click="exportTemplate">下载模板</a> -->
      <el-button @click="exportTemplate">下载</el-button>
    </div>
    <div class="upload_btn">
      <span>上传图片</span>
      <input
        type="file"
        multiple
        accept="image/gif, image/jpeg, image/png, image/jpg"
        @change="uploadImg($event)"
      />
    </div>
  </div>
</template>

<script>
import { upload, download } from "@/api/upload";
export default {
  methods: {
    uploadImg(ev) {
      var files = ev.target.files;
      let formData = new FormData();

      if (!files || files.length === 0) return;
      for (var i = 0; i < files.length; i++) {
        formData.append("files", files[i]);
      }

      upload(formData)
        .then(res => {
          alert("上传成功");
        })
        .catch(() => {});
    },
    exportTemplate() {
        debugger
      let baseURL = process.env.BASE_API;
      let url = baseURL + "file/download";
      alert(url)
      window.location.href = url;
    },
    exportTemplate1() {
      //download().then(res => {});
      download()
        .then(res => {
          //     debugger;
          //   if (!res) {
          //     return;
          //   }
          //   const url = window.URL.createObjectURL(new Blob([res]));
          //   const link = document.createElement("a");
          //   link.style.display = "none";
          //   link.href = url;
          //   link.setAttribute("download", "user.xlsx");
          //   document.body.appendChild(link);
          //   link.click();
        })
        .catch(err => {
          // this.$message.error(err);
        });
    }
  }
};
</script>

(2)还可以把选择文件和上传分开为两步、并且formData携带其他的参数:

<!-- @format -->

<template>
  <div class="container">
    <el-row class="file-div">
      <el-form>
        <el-form-item label="附件:" label-width="120px">
          <!--上传-->
          <el-row>
            <el-col :span="8" class="file-operate">
              <label class="button text-overflow" for="messageFile">
                <Icon type="ios-cloud-upload-outline" size="16" />
                选择文件
              </label>
              <input
                :disabled="isDisable"
                multiple
                ref="messageFile"
                @change="fileHandler()"
                type="file"
                id="messageFile"
                style="position: absolute; clip: rect(0 0 0 0)"
                accept=".png, .jpeg, .jpg, application/pdf, .txt, .xls, .xlsx, .doc, .docx, .wps, .rtf, .ppt, .pptx, .pps, .ppsx,.mp4,.mp3,.zip"
              />
            </el-col>
            <el-col :span="4">
              <el-button
                @click="upload"
                :disabled="isDisable"
                size="small"
                class="submit-btn"
                >上传</el-button
              ></el-col
            >
          </el-row>

          <!--回显-->
          <el-row v-for="(item, index) in files" :key="index">
            <el-col :span="12"
              ><span class="file-name">{{ item.fileName }}</span></el-col
            >
            <el-col :span="2">
              <a @click="downloadFile(item.downloadUrl)" v-if="item.downloadUrl"
                >下载</a
              >
            </el-col>
            <el-col :span="2">
              <a @click="delFile(item.fileName)" v-if="!isDisable"
                >删除</a
              ></el-col
            >
          </el-row>
        </el-form-item>
      </el-form>
    </el-row>
  </div>
</template>
<script>
export default {
  props: { id: {}, isDisable: { default: false }},
  data() {
    return {
      files: [],
      selectFiles: []
    };
  },
  created() {
    if (this.id != null) {
      //id不会空,后台查询回显
      this.getFiles();
    }
  },
  watch: {
    //变化则传给后台
    files: {
      handler: function() {
        var _this = this;
        this.$emit('fileChange', _this.files);
      },
      deep: true
    }
  },
  methods: {
    //回显
    getFiles() {
      this.$api['user/getFiles']({
        formId: this.id
      }).then(data => {
        this.files = data;
      });
    },
    //change
    fileHandler() {
      let currFiles = this.$refs.messageFile.files;
      for (let i = 0; i < currFiles.length; i++) {
        this.selectFiles.push(currFiles[i]);
      }
      for (let i = 0; i < currFiles.length; i++) {
        this.files.push({ fileName: currFiles[i].name });
      }
    },
    //上传
    async upload() {
      let formData = new FormData();
      formData.append('formId', this.id);

      //此次需要上传的文件
      let fileSise = 0;
      for (let i = 0; i < this.selectFiles.length; i++) {
        formData.append('files', this.selectFiles[i]);
        fileSise += this.selectFiles[i].size;
      }
      if (fileSise >= 104857600) {
        this.$Message.error('文件总和不得大于100M');
        document.querySelector('#messageFile').reset();
        return;
      }
      //已有的文件
      let fileNameContextIdMap = new Map();
      this.files.forEach(item => {
        let contextId = item.contextId;
        if (contextId != null) {
          fileNameContextIdMap.set(item.fileName, contextId);
        }
      });
      //map转json
      if (fileNameContextIdMap.size > 0) {
        let obj = Object.create(null);
        for (let [k, v] of fileNameContextIdMap) {
          obj[k] = v;
        }
        formData.append('fileNameContextIdMap', JSON.stringify(obj));
      }
      //上传接口
      await this.$api['file/uploadFile'](formData, {
        headers: {
          'Content-Type': 'multipart/form-data'
        },
        timeout: 300000
      })
        .then(data => {
          // this.$Message.success({
          //   content: '上传成功!',
          //   duration: 3,
          //   closable: true
          // });
        })
        .finally(() => {
          this.selectFiles = [];
          this.getFiles();
        });
    },
    //下载
    downloadFile(downloadFile) {
      let downloadElement = document.createElement('a');
      let href = downloadFile;
      downloadElement.href = href;
      document.body.appendChild(downloadElement);
      downloadElement.click(); //点击下载
      document.body.removeChild(downloadElement); //下载完成移除元素
      window.URL.revokeObjectURL(href); //释放掉blob对象
    },
    //删除文件
    delFile(fileName) {
      this.files = this.files.filter(item => {
        return item.fileName !== fileName;
      });
      //待上传的文件
      let handleFiles = [];
      for (let i = 0; i < this.selectFiles.length; i++) {
        let selectFilName = this.selectFiles[i].name;
        if (fileName != selectFilName) {
          handleFiles.push(this.selectFiles[i]);
        }
      }
      this.selectFiles = handleFiles;
    }
  }
};
</script>

 注意:

(1)以上下载是前端get请求下载文件到本地,如果是post请求,需要这样:

 在请求参数后加:

{ responseType: 'blob' }

 再构造下载: 

let downloadElement = document.createElement('a');
            let blob = new Blob([res.data], { type: 'application/vnd.ms-excel;charset=utf-8' });
            let href = URL.createObjectURL(blob);
            downloadElement.href = href;
            document.body.appendChild(downloadElement);
            downloadElement.click(); //点击下载
            document.body.removeChild(downloadElement); //下载完成移除元素
            window.URL.revokeObjectURL(href); //释放掉blob对象

如我的:

 // 导出
    exportExcel() {
      this.$Modal.confirm({
        title: '导出提示',
        content: `是否导出?`,
        onOk: () => {
          let params = {
            provinceId: this.searchData.provinceId,
            cityId: this.searchData.cityId,
            districtId: this.searchData.districtId
          };
          this.$api['user/exportExcel'](params,{ responseType: 'blob' }).then(res => {
            let downloadElement = document.createElement('a');
            let blob = new Blob([res.data], { type: 'application/vnd.ms-excel;charset=utf-8' });
            let href = URL.createObjectURL(blob);
            downloadElement.href = href;
            document.body.appendChild(downloadElement);
            downloadElement.click(); //点击下载
            document.body.removeChild(downloadElement); //下载完成移除元素
            window.URL.revokeObjectURL(href); //释放掉blob对象
          });
        }
      });
    },

(2)清空file文件的方法:不可以使用$refs的reset方法,可以使用

this.$refs.batchImport.value = '';

 或者

ev.target.value = '';

二、后端:

1、不拦截下载接口:

  /**白名单
     * 需要放行的URL
     */
    private static final String[] AUTH_WHITELIST = {
            "/v2/api-docs",
            "/swagger-resources",
            "/swagger-resources/**",
            "/configuration/ui",
            "/configuration/security",
            "/swagger-ui.html",
            "/static",
            "/static/diagram-viewer",
            "/static/**",
            "/index.html",
            "/code/**",  //验证码
            "/user/logout/**",
            "/file/download/**"

    };

2、接口: 

(1)接收文件数组

package com.demo.rest;
import com.demo.dto.JWTUserDTO;
import com.demo.dto.ResponseMessage;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;

@RequestMapping("/file")
@RestController
public class UploadController {

   //上传
    @RequestMapping("/upload")
    public ResponseMessage uploadFile(@RequestBody MultipartFile[] files) throws IOException {
        JWTUserDTO jwtUser = (JWTUserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        for (MultipartFile file : files) {
            long size = file.getSize();
            String fileName = file.getOriginalFilename();
            System.out.println(fileName + " " + size);
        }
        return ResponseMessage.success("上传成功");
    }


    //下载工程模板文件到前端浏览器
    @RequestMapping("/download")
    public ResponseMessage download(HttpServletResponse response, HttpServletRequest request) {
        String fileName = "user.xlsx";
        File file = new File(this.getClass().getClassLoader().getResource("")
                .getPath() + File.separator + "templates" + File.separator + "user.xlsx");
        response.setContentType("application/octet-stream");
        response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
        byte[] buffer = new byte[1024];
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        try {
            fis = new FileInputStream(file);
            bis = new BufferedInputStream(fis);
            OutputStream os = response.getOutputStream();
            int i = bis.read(buffer);
            while (i != -1) {
                os.write(buffer, 0, i);
                i = bis.read(buffer);
            }
            bis.close();
            fis.close();
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseMessage.success("导出失败");
        } finally {
            try {
                bis.close();
                fis.close();
            } catch (Exception e) {
                return ResponseMessage.success("导出失败");
            }
        }
        return ResponseMessage.success("导出成功");
    }
}

(2)只接收一个文件,去掉数组即可:

@PostMapping("upload")
	public ResponseMessage uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
        String result = fileService.uploadFile(file);
		if (StringUtils.isBlank(result)){
			return ResponseMessage.error("上传失败");
		}else{
			return ResponseMessage.success("上传成功",result);
		}
	}

(3)如果还携带其他参数:使用@ModelAttribute接收对象

@PostMapping(value = "/uploadFile")
    public ResponseMessage uploadFile(@ModelAttribute MaterialVO materialVO)





public class MaterialVO{
   private Integer formId;
   private MultipartFile[] files;
   ...get set方法...
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w_t_y_y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值