vue上传文件到FTP,从FTP下载文件到浏览器 代码参考

3,022 阅读5分钟

1、前端

1.1、上传参考代码

image.png

<template>
  <div>
    <!--    台账导入框-->
    <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" center append-to-body
      @close='handleCancle'>
      <!--      文件导入位置-->
      <el-upload ref="uploada" drag action="" :http-request="voidOne" :on-change="handlFileChange"
        :file-list="upload.fileList" :auto-upload="false">
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">将文件拖到此处,或<em>点击导入</em></div>
      </el-upload>

      <el-input v-model="filePath" clearable placeholder="请输入路径" />

      <div slot="footer">
        <el-button @click="handleCancle">取 消</el-button>
        <el-button type="primary" @click="submitFileUpload">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { uploadFtp } from '@/api/evgis/evImport'

export default {
  props: ["ftpUploadFlag"],
  data() {
    return {
      // 上传参数
      upload: {
        // 标题
        title: '文件上传',
        // 台账导入框
        open: this.ftpUploadFlag,
        // 是否禁用上传
        isUploading: false,
        // 上传的文件列表
        fileList: []
      },
      // 存储路径
      filePath: "",
    }
  },

  methods: {

    // 关闭弹窗
    handleCancle() {
      this.$emit('handleFtpUpload', false);
    },

    // 不使用elementui自带的上传设置
    voidOne() {
      return
    },
    // 所选文件改变时触发
    handlFileChange(file, fileList) {
      this.upload.fileList = fileList
    },
    // 上传文件
    submitFileUpload() {
      this.upload.isUploading = true
      // 上传前校验
      // 校验是否上传文件
      if (this.upload.fileList.length <= 0) {
        this.$message.error('请先选择上传文件')
        this.upload.isUploading = false
        return
      }
      // 校验文件格式
      let whiteList = ['jpg', 'png', 'doc', 'docx', 'txt']
      this.upload.fileList.forEach(item => {
        let fileSuffix = item.raw.name.substring(item.raw.name.lastIndexOf('.') + 1)
        if (whiteList.indexOf(fileSuffix) === -1) {
          whiteList = ''
          return
        }
      })
      if (whiteList === '') {
        this.$message.error('上传文件只能是 jpg, png, doc, docx, txt 格式')
        return
      }
      // 校验是否选择了参数
      if (this.filePath === '') {
        this.$message.error('存储路径不能为空')
        return
      }
      // 定义请求参数
      let formData = new FormData()
      this.upload.fileList.forEach(item => {
        formData.append('file', item.raw)
      })
      
      var filePath = this.filePath;
      // 设置首字符是 反斜杠 /
      if (filePath.substr(0, 1) !== '/') {
        filePath = '/' + filePath;
      }

      // 把所有的 反斜杠转义一下
      var path = filePath.replace('///g', '\/');

      formData.append('filePath', path)

      uploadFtp(formData).then((res) => {

        if (res.code == 200) {
          this.$message.success('上传成功')
        }

        // this.$refs.uploada.clearFiles()
        this.upload.fileList = ''
        this.upload.isUploading = false
        // 重置参数
        this.filePath = ''
      })

      this.handleCancle();
    }
  },

  mounted() {
    console.log();
  },
}
</script>

evImport.js文件

import request from '@/utils/request'

// 从ftp中下载内容
export function downloadFtp(fileName) {
  return request({
    url: '/evgis/import/downloadFtp',
    method: 'post',
    params: {
      fileName: fileName
    },
    responseType: 'blob',
  })
}

1.2、下载参考代码

image.png

<template>
  <div>
    <!--    台账导入框-->
    <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" center append-to-body
      @close='handleCancle'>

      <el-input v-model="filePath" clearable placeholder="请输入路径" />
      <el-input v-model="fileName" clearable placeholder="请输入文件名称" />

      <div slot="footer">
        <el-button @click="handleCancle">取 消</el-button>
        <el-button type="primary" @click="submitFileDown">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { downloadFtp } from "@/api/evgis/evImport"

export default {
  props: ["ftpDownLoadFlag"],
  data() {
    return {
      filePath: '',
      fileName: '',
      // 上传参数
      upload: {
        // 标题
        title: '文件下载',
        // 台账导入框
        open: this.ftpDownLoadFlag,
        // 是否禁用上传
        isUploading: false,
        // 上传的文件列表
        fileList: []
      },
      // 坐标系类型
      coordinateType: "",
    }
  },

  methods: {

    // 关闭弹窗
    handleCancle() {
      this.$emit('handleFtpDown', false);
    },

    // 不使用elementui自带的上传设置
    voidOne() {
      return
    },
    // 所选文件改变时触发
    handlFileChange(file, fileList) {
      this.upload.fileList = fileList
    },
    // 上传文件
    submitFileDown() {

      if (this.filePath === '' || this.fileName === '') {
        this.$message.error('参数不能为空');
        return;
      }

      var filePath = this.filePath;

      // 设置首字符是 反斜杠 /
      if (filePath.substr(0, 1) !== '/') {
        filePath = '/' + filePath;
      }

      // 设置尾字符是 反斜杠 /
      if (filePath.substr(filePath.length - 1, filePath.length) !== '/') {
        filePath = filePath + '/';
      }

      // 把所有的 反斜杠转义一下
      var path = filePath.replace('///g', '\/');
      var name = this.fileName;

      // 下载内容
      downloadFtp(path + name).then((response) => {

        let blob = new Blob([response]);
        const downUrl = URL.createObjectURL(blob);
        const link = document.createElement("a");
        link.style.display = "none";
        link.href = downUrl;
        link.target = "_blank";
        link.download = name;
        document.body.appendChild(link);
        link.click();
        link.remove();
        URL.revokeObjectURL(downUrl); // 释放掉blob对象
      })

      this.handleCancle();
    }
  },

  mounted() {
    console.log();
  },
}
</script>

2、后端

2.1、工具类 - FTPUtils

package com.evimage.web.evgis.ftp;

import com.evimage.common.utils.uuid.UUID;
import lombok.Data;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * 
 * @description: FTP工具对象
 * @author: wangzhan
 * @create: 2022-07-01 11:50
 **/
@Component
@Data
public class FTPUtils {

    @Value("${ftp.host}")
    private String host;

    @Value("${ftp.port}")
    private Integer port;

    @Value("${ftp.username}")
    private String username;

    @Value("${ftp.password}")
    private String password;

    private FTPClient ftpClient = null;

    /**
     * 连接FTP服务器
     */
    public void getFtpConnect(){
        ftpClient = new FTPClient();
        try {
            ftpClient.setControlEncoding("GBK");
            ftpClient.connect(host, port);
            ftpClient.login(username, password);
            int reply = ftpClient.getReplyCode();

            if (!FTPReply.isPositiveCompletion(reply)) {
                closeConnect();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 关闭ftp链接
     */
    public void closeConnect() {
        try {
            ftpClient.logout();
            if (ftpClient.isConnected()) {
                ftpClient.disconnect();
            }
            this.ftpClient = null;
        } catch (IOException e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }

    /**
     * 判断路径是否存在,存在则改变工作目录
     * @param path
     * @return
     */
    public boolean isDirExist(String path){
        boolean flag = false;
        try {
            flag = ftpClient.changeWorkingDirectory(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * 逐级创建工作目录,并跳转到工作目录中
     * @param path
     */
    public void mkdirAndChangeWorkingDirectory(String path){
        for (String str : path.split("/")) {
            if(StringUtils.isBlank(str)) {
                continue;
            }
            try {
                if (!ftpClient.changeWorkingDirectory(str)) {
                    // 创建目录
                    ftpClient.makeDirectory(str);
                    // 改变目录
                    ftpClient.changeWorkingDirectory(str);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 下载文件
     * @param path
     * @return
     */
    public File download(String path) throws IOException {

        InputStream inputStream = null;
        File file = new File(String.valueOf(UUID.randomUUID()));

        try {
            //将设置文件编码
            ftpClient.setControlEncoding(StandardCharsets.UTF_8.name());
            //将设置文件传输模式为二进制,可以保证传输的内容不会被改变
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);

            // 下载文件
            inputStream = ftpClient.retrieveFileStream(path);

            FileUtils.copyInputStreamToFile(inputStream, file);

            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return file;
    }

    /**
     * 将文件上传到ftp上
     * @param file  需要上传的文件
     * @param path  ftp中文件的路径
     * @param fileName  文件名字
     * @return  返回文件存放路径
     */
    public String upload(MultipartFile file, String path, String fileName){

        try {
            //进入本地被动模式
            ftpClient.enterLocalPassiveMode();
            //将设置文件传输模式为二进制,可以保证传输的内容不会被改变
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            //storeFile 上传FTP
            boolean flag = ftpClient.storeFile(fileName, file.getInputStream());

            if (!flag){
                throw new RuntimeException("FTP上传失败!");
            }else {
                System.out.println(fileName+"上传成功");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return path + "/" + fileName;
    }

}

2.2、工具类 - HttpsUtils

package com.evimage.web.evgis.ftp;

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

/**
 * 
 * @description: https相关操作
 * @author: wangzhan
 * @create: 2022-06-22 11:17
 **/

public class HttpsUtils {

    /**
     * 输入文件流及文件名称返回给浏览器下载
     * @param response  浏览器响应对象
     * @param fileInputStream   文件流
     * @param fileName  文件名称
     */
    public static void HttpsDownload(HttpServletResponse response, FileInputStream fileInputStream, String fileName){
        try{
            // 文件路径及名称分割
            String[] split = fileName.split("/");

            //  清空response
            response.reset();
            //Content-Disposition的作⽤:告知浏览器以何种⽅式显⽰响应返回的⽂件,⽤浏览器打开还是以附件的形式下载到本地保存表⽰以附件⽅式下载
            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(split[split.length - 1], "UTF-8"));
            //告知浏览器⽂件的⼤⼩
            response.addHeader("Content-Length", ""+fileInputStream.available());
            response.addHeader("Access-Control-Allow-Origin", "*");
            OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
            response.setContentType("application/octet-stream;charset=UTF-8");
            byte[] buffer = new byte[2048];
            int bytesRead = -1;
            while ((bytesRead = fileInputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            fileInputStream.close();
            outputStream.write(buffer);
            outputStream.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    public static void HttpsReview(HttpServletResponse response, InputStream inputStream,String filename){
        try{
            //  清空response
            response.reset();
            //设置response的Header
            response.setCharacterEncoding("UTF-8");
            //Content-Disposition的作⽤:告知浏览器以何种⽅式显⽰响应返回的⽂件,⽤浏览器打开还是以附件的形式下载到本地保存表⽰以附件⽅式下载
            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
            //告知浏览器⽂件的⼤⼩
            response.addHeader("Content-Length", "" + inputStream.available());
            response.addHeader("Access-Control-Allow-Origin", "*");
            OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
            response.setContentType("application/octet-stream");
            byte[] buffer = new byte[2048];
            int bytesRead = -1;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            outputStream.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

2.3、配置文件 - application.yml

image.png

ftp:
  host: IP地址
  port: 21
  username: evFTP
  password: evFTP

2.4、上传文件后端参考代码

image.png

@Autowired
private FTPUtils ftpUtils;
/**
 * @Description 上传文件至FTP
 * @Author  wangzhan
 * @Date    14:09 2022/8/2
 * @param  file    文件
 * @param  filePath    保存的路径
 *
 * @return  com.evimage.common.core.domain.AjaxResult
 */
@PostMapping("uploadFile")
public AjaxResult upload(@RequestParam("file") MultipartFile file, @RequestParam("filePath") String filePath) {
    try {
        // 1.1. 原始文件名
        String originalFilename = file.getOriginalFilename();

        // 获取ftp连接
        ftpUtils.getFtpConnect();

        // 创建文件夹
        ftpUtils.mkdirAndChangeWorkingDirectory(filePath);
        // 文件上传
        String filePathStr = ftpUtils.upload(file, filePath, originalFilename);

        // 关闭连接
        ftpUtils.closeConnect();

        return AjaxResult.success("上传成功",filePathStr);
    } catch (Exception e) {
        // 关闭连接
        ftpUtils.closeConnect();

        return AjaxResult.error("上传失败" + e.getMessage());
    }
}

2.5、下载文件后端参考代码

image.png

/**
 * @Description 下载ftp文件
 * @Author  wangzhan
 * @Date    14:07 2022/8/2
 * @param  fileName   文件路径 + 文件名称
 * @param  response
 *
 * @return  void
 */
@PostMapping("downloadFtp")
public void downloadFtp(@RequestParam("fileName") String fileName, HttpServletResponse response){
    try {
        // 获取连接
        ftpUtils.getFtpConnect();

        // 去FTP中查找文件并返回
        File file = ftpUtils.download(fileName);

        FileInputStream fileInputStream = new FileInputStream(file);

        HttpsUtils.HttpsDownload(response, fileInputStream, fileName);

        // 关闭流
        fileInputStream.close();
        file.delete();

        // 关闭连接
        ftpUtils.closeConnect();

    }catch (Exception e){
        System.out.println("异常信息:" + e.getMessage());
    }
}