2022-04-13_有赞表单组件van-uploader文件上传前后台学习笔记

20220413_有赞表单组件van-uploader文件上传前后台学习笔记

1概述

van-uploader用于将本地的图片或文件上传至服务器,并在上传过程中展示预览图和上传进度。目前 Uploader 组件不包含将文件上传至服务器的接口逻辑,该步骤需要自行实现。

目前 Chrome、Safari 等浏览器不支持展示 HEIC/HEIF 格式的图片,因此上传后无法在 Uploader 组件中进行预览。

文件上传的核心点:构建FormData,将文件以表单的形式发送出去。

本文主要基于该组件进行全流程功能实现。

1.1van-uploader

1.1.1API

Props

参数 说明 类型 默认值
v-model (fileList) 已上传的文件列表 FileListItem[] -
accept 允许上传的文件类型,详细说明 string image/*
name 标识符,可以在回调函数的第二项参数中获取 number | string -
preview-size 预览图和上传区域的尺寸,默认单位为 px number | string 80px
preview-image 是否在上传完成后展示预览图 boolean true
preview-full-image 是否在点击预览图后展示全屏图片预览 boolean true
preview-options v2.9.3 全屏图片预览的配置项,可选值见 ImagePreview object -
multiple 是否开启图片多选,部分安卓机型不支持 boolean false
disabled 是否禁用文件上传 boolean false
readonly v2.12.26 是否将上传区域设置为只读状态 boolean false
deletable 是否展示删除按钮 boolean true
show-upload v2.5.6 是否展示上传区域 boolean true
lazy-load v2.6.2 是否开启图片懒加载,须配合 Lazyload 组件使用 boolean false
capture 图片选取模式,可选值为 camera (直接调起摄像头) string -
after-read 文件读取完成后的回调函数 Function -
before-read 文件读取前的回调函数,返回 false 可终止文件读取, 支持返回 Promise Function -
before-delete 文件删除前的回调函数,返回 false 可终止文件读取, 支持返回 Promise Function -
max-size v2.12.20 文件大小限制,单位为 byte number | string | (file: File) => boolean -
max-count 文件上传数量限制 number | string -
result-type 文件读取结果类型,可选值为 file text string dataUrl
upload-text 上传区域文字提示 string -
image-fit 预览图裁剪模式,可选值见 Image 组件 string cover
upload-icon v2.5.4 上传区域图标名称或图片链接 string photograph

注意:accept、capture 和 multiple 为浏览器 input 标签的原生属性,移动端各种机型对这些属性的支持程度有所差异,因此在不同机型和 WebView 下可能出现一些兼容性问题。

1.1.2事件

1.1.3回调参数

before-read、after-read、before-delete 执行时会传递以下回调参数:

参数名 说明 类型
file file 对象 object
detail 额外信息,包含 name 和 index 字段 object

1.1.3.1file对象格式

file具有的属性如下:
content:文件对应的base64编码
file:文件元信息对象
message:
status:

单个文件格式:

image-20220413212126161.png

多个文件格式:

image-20220414144011520.png

1.4桌面端适配(桌面预览鼠标点击无法关闭,好人)

Vant 是一个面向移动端的组件库,因此默认只适配了移动端设备,这意味着组件只监听了移动端的 touch 事件,没有监听桌面端的 mouse 事件。

如果你需要在桌面端使用 Vant,可以引入我们提供的 @vant/touch-emulator,这个库会在桌面端自动将 mouse 事件转换成对应的 touch 事件,使得组件能够在桌面端使

https://youzan.github.io/vant/v2/#/zh-CN/advanced-usage#zhuo-mian-duan-gua-pei

# 安装模块
npm i @vant/touch-emulator -S
// 引入模块后自动生效
import '@vant/touch-emulator';

2代码示例

2.1前端请求

2.1.1注册Vant(main.js)

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'

// 支持一次性导入所有组件,引入所有组件会增加代码包体积,但比较爽
// 因此不推荐这种做法
// 1.引入 Vant组件
import Vant from 'vant'

// 2.引入 Vant样式
import 'vant/lib/index.css'
// 3.引入拦截器
import './config/httpinterceptor'

Vue.config.productionTip = false
// 3.注册 Vant
Vue.use(Vant)

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: ''
})
// 通过main.js将App.vue渲染到index.html的指定区域中。

2.1.2导入业务组件(main.js)

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'

// 支持一次性导入所有组件,引入所有组件会增加代码包体积,但比较爽
// 因此不推荐这种做法
// 1.引入 Vant组件
import Vant from 'vant'

// 2.引入 Vant样式
import 'vant/lib/index.css'

import './config/httpinterceptor'

Vue.config.productionTip = false
// 3.注册 Vant
Vue.use(Vant)

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: ''
})
// 通过main.js将App.vue渲染到index.html的指定区域中。

2.1.3httpinterceptor拦截器

// 接口请求拦截器
import Vue from 'vue'

import VueResource from 'vue-resource'

Vue.use(VueResource)

// 接口请求拦截器
Vue.http.interceptors.push((request, next) => {
  debugger

  // 1.credentials
  // request.credentials = true;
  // 2.Content_Type
  // 'multipart/form-data'
  // request.headers.get('Content-Type')
  // url:/server/paging -->/api/server/paging
  // 3.判断方法的类型
  // if(/^post$/i.test(request.method))

  console.log(request.url)
  // 每个接口请求拦截前加前缀: /api,这样可以走同一的后台代理
  request.url = '/api' + request.url

  next((response) => {
    // 在响应之后传给then之前对response进行修改和逻辑判断。
    // 对于token时候已过期的判断,就添加在此处,
    // 页面中任何一次 http请求都会先调用此处方法
    if (response.status === 401) {
      console.log('response.body:', response.body)
      window.location.reload()
    }
    return response
  })
})

2.1.4编写业务组件

2.1.4.1编写模板



2.1.4.2编写行为



2.1.4.2.1afterUploader核心发送函数分析

请特别注意headers设置。

afterUploader (file) {
      debugger
      // 此时可以自行将文件上传至服务器
      console.log(file)
      // 1.添加文件类型请求头
      let httpOption = {
        headers: {
          // 添加文件类型请求头
          'Content-Type': 'multipart/form-data'
        }
      }
      // 2.构建 FormData
      let formData = new FormData()
      // 单个文件:
      formData.append('file', file.file, file.filename)
      // 多个文件:
      // for (let singlefile of file) {
      //   // 分多次向formData中同一个键名下添加一个文件即可
      //   formData.append('files', singlefile.file, singlefile.filename)
      // }
      // 3.发送
      // vue-resource
      this.$http.post('/file/upload', formData, httpOption).then(({body}) => {
        debugger
      }).catch(() => {
        debugger
      })
    }
image-20220413211038593.png

2.1.4.3编写样式




2.2后台处理

2.2.1FileController

package com.kikop.controller;

import com.alibaba.fastjson.JSONObject;
import com.kikop.service.FileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;

/**
 * @author kikop
 * @version 1.0
 * @project myantbackdemo
 * @file FileController
 * @desc
 * @date 2022/04/13
 * @time 20:39
 * @by IDE: IntelliJ IDEA
 */
@RestController
@RequestMapping("/api/file")
public class FileController {


    @Autowired
    FileService fileService;

    // web容器临时存储目录:
    // C:\Users\kikop\AppData\Local\Temp\tomcat.1514260066643344745.8086\work\Tomcat\localhost\ROOT\
    @RequestMapping("/upload")
    public JSONObject upload(@RequestParam(value = "file", required = true) MultipartFile file, HttpServletRequest request) {
        return fileService.upload(file, request);
    }

    @RequestMapping("/upload")
    public JSONObject multipleUpload(@RequestParam(value = "files", required = true) MultipartFile file, HttpServletRequest request) {
        return fileService.upload(file, request);
    }

}

2.2.2FileServiceImpl

package com.kikop.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.kikop.myconst.CommonResponse;
import com.kikop.myconst.ConstVarManager;
import com.kikop.service.FileService;
import com.kikop.utils.MyFileUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;

@Service
public class FileServiceImpl implements FileService {

    @Override
    public JSONObject upload(MultipartFile multipartFile, HttpServletRequest request) {


        // tomcat临时文件
        // C:\Users\kikop\AppData\Local\Temp\tomcat-docbase.5125138269731218182.8086\
        // String realPath = request.getSession().getServletContext().getRealPath("/");

        // 1.生成文件名
        String strDestFileName = UUID.randomUUID().toString().replace("-", "");

        // 2.文件元信息
        String originalFilename = multipartFile.getOriginalFilename();

        try {
            JSONObject result = new JSONObject();
            result.put("success", true);
            // 3.文件保存
            String fileType = originalFilename.substring(originalFilename.lastIndexOf("."));
            if (StringUtils.isEmpty(fileType)) {
                result.put("success", false);
                result.put("msg", "文件类型不合法!");
                return result;
            }
            // C:\Users\kikop/myuploadfile\
            String strDestFilePathName_first = ConstVarManager.MyUploadFile_Name;
            // 2ea0b69709e947c48984acf248d077f5.png
            String strDestFilePathName_more = strDestFileName + fileType;
            System.out.println("文件存储全路径为:" + strDestFilePathName_first + strDestFilePathName_more);
            MyFileUtils.copyFile2DestDirectory(multipartFile.getInputStream(), strDestFilePathName_first, strDestFilePathName_more);

            // http://localhost:8086/myuploadfile/2ea0b69709e947c48984acf248d077f5.png
            // 获取服务上下文
            String requestContextPath = request.getScheme() + "://" + request.getServerName() + ":"
                    + request.getServerPort() + "/" + request.getContextPath();
            // http://localhost:8086/
            result.put("destContextPath", requestContextPath);
            // myuploadfile/2ea0b69709e947c48984acf248d077f5.png
            result.put("destFileName", ConstVarManager.MyUploadFile_VisitName + File.separator + strDestFilePathName_more);
            // 4.返回
            return result;
        } catch (Exception ex) {
            ex.printStackTrace();
            return CommonResponse.getCommonResponse(ex);
        }
    }
}

2.2.3ConstVarManager

package com.kikop.myconst;

import java.io.File;

public class ConstVarManager {

    public static final String MyUploadFile_Protocol = "file:///";
    public static final String MyUploadFile_VisitName = "myuploadfile";

    // C:\Users\kikop/myuploadfile/
    public static final String MyUploadFile_Name = System.getProperties().getProperty("user.home") + File.separator +
            MyUploadFile_VisitName + File.separator;

    static {

//        自动创建目录判断
//        https://www.cnblogs.com/qbdj/p/10980840.html
        File uploadDirectory = new File(MyUploadFile_Name);
        if (!uploadDirectory.exists()) {
            uploadDirectory.mkdir();
//            uploadDirectory.mkdirs();  //多层目录需要调用mkdirs
        }
    }
}

2.2.4MyFileUtils

package com.kikop.utils;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class MyFileUtils {

    public static void copyFile2DestDirectory(InputStream inputStream, String strDestFilePathName_first, String strDestFilePathName_more) throws IOException {
        Path destFilePath = Paths.get(strDestFilePathName_first, strDestFilePathName_more);
        Files.copy(inputStream, destFilePath);
    }
}

2.2.5WebAppConfig

package com.kikop.config;

import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.kikop.myconst.ConstVarManager;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

/**
 * @author kikop
 * @version 1.0
 * @project myantbackdemo
 * @file
 * @desc
 * @date 2020/10/31
 * @time 21:56
 * @by IDE: IntelliJ IDEA
 */
@Configuration
public class WebAppConfig implements WebMvcConfigurer {


    private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
            "classpath:/META-INF/resources/",
            "classpath:/resources/",
            "classpath:/static/",
            "classpath:/public/"};


    @Override
    public void configureMessageConverters(List> converters) {
        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
        converters.add(fastJsonHttpMessageConverter);
    }

    /**
     * 静态资源(不需要,用默认的即可)
     * 配置请求的解析映射路径
     *
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {


        // 1.默认不配置也行
        // spring mvc默认的
        // http://localhost:8080/myveuback.jpeg
        registry.addResourceHandler("/**").addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS);

        // 2.自定义
        registry.addResourceHandler("/myuploadfile/**")
                .addResourceLocations(ConstVarManager.MyUploadFile_Protocol+ConstVarManager.MyUploadFile_Name);

    }
}

2.3测试

[图片上传失败...(image-6419a0-1649936781086)]

[图片上传失败...(image-34730d-1649936781087)]

参考

1vant uploader组件,回显文件、文件名

https://blog.csdn.net/weixin_42540974/article/details/121539208

2vue 使用vant Uploader 文件上传(图片压缩)

https://blog.csdn.net/weixin_40918145/article/details/108267163

3vant中uploader上传图片

https://blog.csdn.net/weixin_50651378/article/details/123765330

你可能感兴趣的:(2022-04-13_有赞表单组件van-uploader文件上传前后台学习笔记)