【京淘电商】文件上传

一,vue上传文件组件

	<!--文件上传
       action: 图片上传的地址
       file-list: 需要展现的图片列表信息 数组
       :on-preview="handlePreview" 点击链表触发函数
       :on-remove="handleRemove" 点击删除
       drag 拖拽图片
       multiple 批量操作
    -->
    <el-upload
      class="upload-demo"
      action="https://jsonplaceholder.typicode.com/posts/"
      :on-preview="handlePreview"
      :on-remove="handleRemove"
      :file-list="fileList"
      list-type="picture"
      drag
      multiple>
      <el-button size="small" type="primary">点击上传</el-button>
      <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
    </el-upload>

export default {
      data() {
        return {
          fileList: [{name: 'food.jpeg', url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100'}, {name: 'food2.jpeg', url: 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100'}]
        };
      },
      methods: {
            handleRemove(file, fileList) {
              console.log(file, fileList);
            },
            handlePreview(file) {
              console.log(file);
            },
            handleExceed(files, fileList) {
              this.$message.warning(`当前限制选择 3 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
            },
            beforeRemove(file, fileList) {
              return this.$confirm(`确定移除 ${ file.name }?`);
            }
      }
    }

二,文件上传入门案例

1.页面js分析

			<!--
                了解:
                  action: 文件上传提交的地址
                  name:   默认文件上传的名称 file
            -->
            <el-upload class="upload-demo" :action="uploadUrl" :on-preview="handlePreview" :on-remove="handleRemove"
              :on-success="handleSuccess" list-type="picture" multiple drag>
              <el-button size="small" type="primary">点击上传</el-button>
              <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
            </el-upload>

2.业务接口说明

  • 请求路径: http://localhost:8091/file/upload
  • 请求类型: post
  • 请求参数:
参数名称 参数说明 备注
file 文件上传的参数名称 file中携带的是二进制信息
  • 返回值结果:
参数名称 参数说明 备注
status 状态信息 200表示服务器请求成功 201表示服务器异常
msg 服务器返回的提示信息 可以为null
data 服务器返回的业务数据 返回ImageVO对象
  • ImageVO对象说明
参数名称 参数类型 参数说明 备注
virtualPath String 图片实际路径 不包含磁盘信息 例如: 2021/11/11/a.jpg 不需要写磁盘地址
urlPath String 图片url访问地址 http://image.jt.com/2021/11/11/a.jpg 需要指定域名地址
fileName String 文件上传后的文件名称 UUID.type

3.编辑ImageVO

为了封装文件上传之后的结果,需要封装VO对象 格式如下

@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ImageVO implements Serializable {

    private String virtualPath; //虚拟地址,不包含磁盘地址
    private String urlPath;     //URL地址信息.
    private String fileName;    //文件名称
}

4.实现

@RestController
@CrossOrigin
@RequestMapping("/file")
public class FileController {
    /**
     * 业务: 实现文件上传
     * url: /file/upload
     * 请求类型: POST
     * 参数: file
     * 返回值: SysResult(imageVO)
     * 高级API:MultipartFile 自动维护了缓存流/自动开关
     *
     * 文件上传步骤:
     *      1.获取文件名称.
     *      2.准备上传文件的目录
     *      3.封装文件全路径  目录/文件名称
     *      4.实现文件上传
     */
    @PostMapping("/upload")
    public SysResult upload(MultipartFile file) throws IOException {
        //1.获取文件名称  a.jpg
        String fileName = file.getOriginalFilename();
        //2.准备文件目录
        String fileDir = "G:/images/";
        //2.1 判断目录是否存在
        File dir = new File(fileDir);
        if(!dir.exists()){
            //如果目录不存在,则创建多级目录
            dir.mkdirs();
        }
        //3.准备文件全路径
        String localPath = fileDir + fileName;
        //4.实现文件输出
        file.transferTo(new File(localPath));
        System.out.println("文件上传成功!!!!");
        return SysResult.success();
    }
}

三,图片上传业务实现

1.文件上传的注入事项

  • 控制文件上传的类型(后台为主),一般jpg,png,gif
  • 控制恶意程序的上传 通过宽度和高度,恶意程序毕竟是文件,所以不会有高和宽
  • 为了提高检索的速度,应该分目录存储.
  • 动态生成文件名称 UUID,防止文件重名.你上传相同的图片在同一文件夹必然会重名,所以每个图片都必须要唯一的名称
  • 实现文件上传 注意路径.

2.正则表达式

2.1说明

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

2.2语法介绍

1.标识开头/结尾/匹配多次
【京淘电商】文件上传
regex: abc 标识只能匹配固定的字符abc
regex: abc* 标识匹配 ab,c可以任意次.
regex: abc? 标识匹配 ab,c可以1次或者0次.

2.匹配确定次
【京淘电商】文件上传
regex: c{3} c只能出现3次
regex: c{3,} c出现>=3次
regex: c{3,10} c出现>=3 <=10次
regex: .* 匹配所有字符.

3.匹配固定字符
【京淘电商】文件上传
regex:

 a[ab] 匹配2个字符  第一个字符必须为a, 第二个字符 必须a 或者b
 [a-z][0-9] 第一个字符是a-z  第二个字符必须是0-9

4.分组匹配
【京淘电商】文件上传
demo: (png|jpg|gif) 字符要么是png,要么是ipg 要么是gif.

3.编辑FileController

@RestController
@CrossOrigin
@RequestMapping("/file")
public class FileController {

    @Autowired
    private FileService fileService;

    @PostMapping("/upload")
    public SysResult upload(MultipartFile file) throws IOException {

        ImageVO imageVO = fileService.upload(file);
        if(imageVO == null){//说明业务执行有误
            return SysResult.fail();
        }
        return SysResult.success(imageVO);
    }
}

4.编辑FileService

@Service
public class FileServiceImpl implements FileService{

    private String localDir = "G:/images";

    /**
     * 完成校验:
     *  1.校验是否为图片
     *  2.木马.exe.jpg 判断是否满足图片固有属性 高度/宽度
     *  3.为了提高查询效率,要求分目录存储.
     *      3.1 按照后缀名分配  jpg,png,gif 效率提升不能满足要求
     *      3.2 按照日期分   yyyy/MM/dd/HH   可以
     *      3.3 商品分类     出现分布不均现象.
     *      3.4 根据名称hash 之后截串
     *          demo: hash(a)=qw|er|as|dg/a.jpg
     *          弊端: hash码可能出现分布不均的现象.
     *  4.防止文件重名  使用uuid代替名称
     * @param file
     * @return
     */
    @Override
    public ImageVO upload(MultipartFile file) {
        //1.获取图片名称    demo: abc.jpg  abc.JPG
        String fileName = file.getOriginalFilename();
        //bug说明: 由于windows系统不区分大小写,所以将字母全部转化为小写
        fileName = fileName.toLowerCase();
        //利用正则判断是否为图片.
        if(!fileName.matches("^.+\\.(jpg|png|gif)$")){
            //如果不是图片,则返回null
            return null;
        }
        //2.检查文件是否为恶意程序.
        try {
            BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
            int width = bufferedImage.getWidth();
            int height = bufferedImage.getHeight();
            if(width == 0 || height == 0){
                //说明文件不是图片.
                return null;
            }
            //3.根据时间实现目录的创建 时间--yyyy/MM/dd
            String dateDir = new SimpleDateFormat("/yyyy/MM/dd/")
                            .format(new Date());
            // "G:/images/2021/11/11
            String localDirPath = localDir + dateDir;
            //创建目录
            File dirFile = new File(localDirPath);
            if(!dirFile.exists()){
                dirFile.mkdirs();
            }
            //4. 使用uuid替换文件名称 唯一:系统内部唯一
            String uuid = UUID.randomUUID().toString()
                    .replace("-","");
            //截取文件的后缀  aa.bb.cc.jpg
            int index = fileName.lastIndexOf(".");
            //获取类型  .jpg
            String fileType = fileName.substring(index);
            String newFileName = uuid + fileType;
            //5.实现文件上传操作  目录/文件名称
            String realFilePath = localDirPath + newFileName;
            file.transferTo(new File(realFilePath));
            System.out.println("文件上传成功!!!");
            /*
                 6.封装返回值
             *  封装虚拟路径 在各个系统之间可以灵活切换,只保存动态变化的目录
             *  path = 时间/uuid.type
             */
            String virtualPath = dateDir + newFileName;
            String url = "https://img14.360buyimg.com/n0/jfs/t1/157402/13/13529/158789/60517f36E2e8f9939/958bdb78df7c145f.jpg";
            return new ImageVO(virtualPath,url,newFileName);
        } catch (IOException e) {
            e.printStackTrace();
            return null;    //表示程序有问题
        }
    }
}

4.页面效果展现

【京淘电商】文件上传

上一篇:linux 服务器 访问 静态文件的两种方式 nginx和tomcat


下一篇:高敏的第一天