1. 程式人生 > >【Java】檔案上傳及下載、限制檔案大小

【Java】檔案上傳及下載、限制檔案大小

1、後端部分

      Controller程式碼

    // 上傳
    @PostMapping("upload")
    @ResponseBody
    public Result upload(MultipartFile files, Integer id, String type){
        fileService.upload(files, id, type);
        return success().setRet(0);
    }

    // 下載
    @GetMapping("download")
    @ResponseBody
    public void download(HttpServletResponse response, Integer id){
        fileService.download(response, id);
    }

      Service程式碼

    @Value("${fileServer.storeDir}")
    private String storeDir;

    @Autowired
    private FileMapper fileMapper;

    @Override
    public void upload(MultipartFile file, Integer id, String type) {
        logger.info("檔案關聯id:{}, 檔名:{}", id, file.getOriginalFilename());
        logger.info("檔案字尾名:{}", file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")));
        if(!file.isEmpty()){
            String dir = getStoreDir();

            File tempFile = new File(dir + System.currentTimeMillis() + file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")));
            try {
                file.transferTo(tempFile);

                FileInfo fileInfo = new FileInfo()
                        .setName(file.getOriginalFilename())
                        .setPath(tempFile.getAbsolutePath())
                        .setSize(tempFile.length())
                        .setTarget(id)
                        .setCreateDate(new Date())
                        .setMd5(FileUtil.md5(tempFile))
                        .setType(type);
                fileMapper.insert(fileInfo);

            } catch (IOException e) {
                logger.error(e.getMessage(), e);
                throw new InternalServerException("file build error");
            }
        }
    }


    @Override
    public void download(HttpServletResponse response, Integer id) {
        FileInfo fileInfo = fileMapper.findFileById(id);
        logger.info("下載檔名:{}", fileInfo.getName());
        File file = new File(fileInfo.getPath());

        if (!FileUtil.isExistingFile(file)) {
            throw new ResourceNotFoundException("file not found");
        }
        try (OutputStream outputStream = response.getOutputStream();
             InputStream inputStream = new FileInputStream(file)) {

            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileInfo.getName(), "UTF-8"));

            FileCopyUtils.copy(inputStream, outputStream);

        } catch (IOException e) {
            logger.error("下載檔案失敗!",e);
            throw new RemoteServiceUnavailableException("file download error");
        }
    }

    private String getStoreDir(){
        String dir = storeDir + File.separator ;
//        String dir = storeDir + File.separator + System.currentTimeMillis() + File.separator;
        try {
            FileUtil.mkdir(dir);
        } catch (IOException e) {
            logger.error("獲取儲存目錄失敗!",e);
            throw new InternalServerException("file build error");
        }
        return dir;
    }

    private void delFile(String path){
        try {
            FileUtil.delete(path);
        } catch (IOException e) {
            logger.error("刪除檔案失敗!",e);
            throw new InternalServerException("file delete error");
        }

    }

      在application.yml配置檔案中設定檔案儲存路徑(這裡假設是本地的E:\ptms中)、以及限制上傳檔案大小(這裡設定為40M)。

# 應用名稱
spring:
  application:
    name: ptms
  resources:
    static-locations: classpath:/static
    cache:
      period: PT1M        # 60 SECONDS
  servlet:
    multipart:
      max-file-size: 40Mb
      max-request-size: 40Mb

fileServer:
  storeDir: E:\ptms

2、前端部分

      前端程式碼如下,只看核心的上傳下載即可。上傳的控制元件採用element的el-upload控制元件,下載採用的是window.location.href。同時,在表單驗證完之後,提交表單之前,還做了檔案大小的校驗,採用的是遍歷this.$refs.upload.uploadFiles。

<template>
  <div>
    <el-form class="mt10 pd10 elForm" :rules="rules" ref="ruleForm" :model="ruleForm" label-width="130px" :label-position="labelPosition">
      <el-form-item label="專案名稱:" prop="name" :inline-message="true">
        <el-col :span="8">
          <el-input placeholder="請輸入專案名稱" v-model="ruleForm.name"></el-input>
        </el-col>
      </el-form-item>
      <el-form-item label="專案版本:" prop="version">
        <el-col :span="8">
          <el-input placeholder="請輸入專案版本" v-model="ruleForm.version"></el-input>
        </el-col>
      </el-form-item>
      <el-form-item label="產品系列:" prop="productSeriesId">
        <el-col :span="8">
          <el-select v-model="ruleForm.productSeriesId" placeholder="請選擇產品系列" @change="chooseProductSeries" filterable clearable size="medium">
            <el-option v-for="item in productSeries" :key="item.id" :label="item.name" :value="item.id"></el-option>
          </el-select>
        </el-col>
      </el-form-item>
      <el-form-item label="專案起止:" prop="date1">
          <el-date-picker v-model="ruleForm.date1" type="daterange" range-separator="至" start-placeholder="開始日期" end-placeholder="結束日期" value-format="yyyy-MM-dd">
          </el-date-picker>
      </el-form-item>
      <el-form-item label="專案描述:" prop="description">
        <el-col :span="8">
          <el-input placeholder="請輸入專案描述" v-model="ruleForm.description" type="textarea"></el-input>
        </el-col>
      </el-form-item>
      <el-form-item label="操作備註:" v-if="this.$route.params.id != null">
        <el-col :span="8">
          <el-input placeholder="請輸入操作備註" v-model="ruleForm.remark" type="textarea"></el-input>
        </el-col>
      </el-form-item>
      <el-form-item label="已上傳附件:" v-if="fileUploaded">
        <el-col :span="11">
          <template>
            <el-table :data="tableData" border>
              <el-table-column prop="id" label="檔案id" v-if="false"></el-table-column>
              <el-table-column prop="name" label="檔名稱" min-width="180" show-overflow-tooltip>
                <template slot-scope="scope">
                  <a href="javascript:" @click="downloadFile(scope.row.id)" class="aLink">{{scope.row.name}}</a>
                </template>
              </el-table-column>
              <el-table-column prop="size" label="檔案大小" min-width="100" show-overflow-tooltip></el-table-column>
              <el-table-column prop="createDate" label="上傳時間" min-width="120" show-overflow-tooltip></el-table-column>
              <el-table-column label="操作" min-width="100" show-overflow-tooltip>
                <template slot-scope="scope">
                  <el-button type="text" @click="downloadFile(scope.row.id)">下載</el-button>
                  <el-button type="text" @click="deleteFile(scope.row.id)">刪除</el-button>
                </template>
              </el-table-column>
            </el-table>
          </template>
        </el-col>
      </el-form-item>
      <el-form-item label="附件上傳:">
        <el-col :span="8">
          <el-upload class="upload-demo" ref="upload" name="files" :data="uploadData" action="/ptms/api/v1/File/upload" multiple
            :on-preview="handlePreview" :on-remove="handleRemove" :on-success="handleSuccess" :on-error="handleError" :auto-upload="false">
            <el-button slot="trigger" size="small" type="primary">選取檔案</el-button>
            <span slot="tip" class="el-upload__tip" style="margin-left: 20px; font-weight: bold">單個檔案大小不超過{{maxFileSize}}}M</span>
          </el-upload>
        </el-col>
      </el-form-item>
      <el-form-item>
        <template slot-scope="scope">
          <el-button type="primary" @click="submit('ruleForm')" class="btn-min-w">提交</el-button>
          <el-button @click="cancel()"  class="btn-min-w">返回</el-button>
        </template>
      </el-form-item>
    </el-form>
  </div>
</template>
<script>
  import tableOperation from '@/components/layout/tableOperation'
  import  * as api from '@/api/ptms'
  import {tips} from '../../static/js/global.js'
  import moment from 'moment'
  export default {
    inject:['reload'],
    components:{ tableOperation},
    data(){
      return{
        labelPosition: 'right',
        ruleForm: {
          name:'',
          version:'',
          description:'',
          productSeriesId: '',
          date1: [],
          createdBy: sessionStorage.getItem("userId"),
          actor: sessionStorage.getItem("username"),
          remark: '',
        },
        productSeries: [],
        rules: {
          name: [
            { required: true, message: '請輸入專案名稱', trigger: 'blur' }
          ],
        },
        uploadData:{
          id: null,
          type: 'project'
        },
        id: null,
        tableData: [{}],
        fileUploaded: false,
        maxFileSize: 40,
      }
    },
    created() {
      // 跳轉頁面獲取資訊
      api.projectForm({id: this.$route.params.id})
        .then((res)=>{
          if(res.ret > -1){
            console.log(res.data)
            this.productSeries = res.data.productSeries;
             if(this.$route.params.id != null){
              this.ruleForm.name = res.data.project.name;
              this.ruleForm.version = res.data.project.version;
              this.ruleForm.description = res.data.project.description;
              this.ruleForm.productSeriesId = res.data.project.productSeriesId;
              this.ruleForm.createdBy = res.data.project.createdBy;
              if(res.data.project.startTime != null){
                this.ruleForm.date1.push(new Date(res.data.project.startTime));
              }
              if(res.data.project.endTime != null){
                this.ruleForm.date1.push(new Date(res.data.project.endTime));
              }

              this.id = this.$route.params.id;

              this.showFileList(this.id);
            }
          }
        })
    },
    methods:{

      chooseProductSeries(val){
        this.ruleForm.productSeriesId = val;
      },

      // 新增、修改專案
      submit(formName) {
        this.$refs[formName].validate((valid) => {
          if(valid){
            //檔案大小校驗
            let flag = true;
            this.$refs.upload.uploadFiles.map((item) => {
              if(item.size / 1024 / 1024 > this.maxFileSize){
                tips(this, item.name + '超過允許檔案大小' + this.maxFileSize + 'M,請重新選擇!', 'warning');
                flag = false;
              }
            });
            if(flag){
              const id = (typeof (this.$route.params.id) === 'undefined') ? null : this.$route.params.id;
              let startTime = null;
              let endTime = null;
              if(this.ruleForm.date1 != null && this.ruleForm.date1.length > 1){
                if(typeof this.ruleForm.date1[0] === 'object'){
                  startTime = moment(this.ruleForm.date1[0]).format("YYYY-MM-DD");
                  endTime = moment(this.ruleForm.date1[1]).format("YYYY-MM-DD");
                }else{
                  startTime = this.ruleForm.date1[0];
                  endTime = this.ruleForm.date1[1];
                }
              }

              api.projectFormAction({id: id, name: this.ruleForm.name, version: this.ruleForm.version, createdBy: this.ruleForm.createdBy,
                productSeriesId: this.ruleForm.productSeriesId, description:this.ruleForm.description, startTime: startTime,
                endTime:endTime, actor: this.ruleForm.actor, remark: this.ruleForm.remark})
                .then((res)=>{
                  console.log("專案資訊", res.data)
                  if(res.data === "existed"){
                    tips(this, '該專案名稱已存在', 'warning');
                    return;
                  }
                  //上傳檔案
                  if(this.$refs.upload.uploadFiles.length > 0){
                    this.uploadData.id = res.data;
                    this.submitUpload();
                  }else {
                    tips(this, this.id == null ? '新增成功!' :'修改成功!', 'success');
                    this.$router.push('/projectList');
                  }

                })

            }
          }
        })
      },

      // 下載檔案
      downloadFile(val){
        window.location.href = "/ptms/api/v1/File/download?id=" + val;
      },

      // 刪除檔案
      deleteFile(val){
        this.$confirm('確認刪除該附件?', '提示', {
          confirmButtonText: '確定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          api.deleteFile({id: val})
            .then((res) => {
              if(res.ret === 0){
                tips(this, '附件刪除成功!', 'success');
                this.showFileList(this.id);
                // this.reload();
              }
            })
        }).catch(() =>{
          // 取消
        });
      },

      // 展示已上傳檔案列表
      showFileList(id){
        api.getFilesByTarget({target: id, type: this.uploadData.type})
          .then((res) => {
            if(res.data.files.length > 0){
              this.fileUploaded = true;
              res.data.files.forEach((item) => {
                item.size = (item.size / 1024 ).toFixed(2) + " KB";
                item.createDate = moment(item.createDate).format('YYYY-MM-DD HH:mm:ss');
              });
              this.tableData = res.data.files;
            }else {
              this.fileUploaded = false;
            }
          })
      },

      handleSuccess(response, file, fileList){
        console.log("successFile: ", file);
        if(response.ret < 0){
          console.log("response: ", response);
          this.$refs.upload.clearFiles();
          tips(this, '上傳失敗!', 'error');
        }else {
          tips(this, this.id == null ? '新增成功!' :'修改成功!', 'success');
          this.$router.push('/projectList');
        }
      },

      handleError(err, file, fileList){
        console.log("err: ", err);
        console.log("errorFile: ", file);
        tips(this, '上傳失敗!', 'error');
      },

      submitUpload() {
        this.$refs.upload.submit();
      },

      handleRemove(file, fileList) {
        console.log(file, fileList);
      },

      handlePreview(file) {
        console.log(file);
      },

      // 取消返回上一頁
      cancel() {
        this.$router.back(-1);
      }

    }
  }
</script>
<style lang="scss" scoped>
</style>


3、最終效果如下