您的位置:首页 > Web前端 > Vue.js

vue + element-ui + springboot + 阿里云OSS 使用表单进行图片文件等的上传(具有上传滚动条效果)

2020-01-14 15:35 1056 查看

vue + element-ui + springboot + 阿里云OSS 使用表单进行图片文件等的上传(具有上传滚动条效果)

  • 使用vue + elementUI的前端页面中,使用el-upload组件,在添加,或者修改时。在表单中,加入图片。一起传给后台 springboot服务器,将文件上传给 阿里云OSS。

  • 具有上传效果 滚动条效果

  • 小编的代码项目地址:https://github.com/kedaya-github/vue_system

所需工具

  • 开通阿里云 OSS对象存储。创建Bucket

  • 前端vue + elementUI项目 —Visual Studio Code

  • 后端 springboot项目 — idea

  • 以下代码复制,修改配置和属性后可直接使用,话不多说,老表,上手吧。

vue前端

  • 添加数据时,携带一个文件 + 表单基础数据,一起传递给后台:
  • 页面:

html页面

  • 页面代码:

    <el-dialog title="添加品牌" :visible.sync="dialogFormVisible">
    <el-form :model="addBrand" ref="addBrand">
    <el-form-item label="品牌名称">
    <el-input v-model="addBrand.name" autocomplete="off"></el-input>
    </el-form-item>
    <el-form-item label="品牌首字母">
    <el-input v-model="addBrand.letter" autocomplete="off"></el-input>
    </el-form-item>
    <el-form-item label="添加图片" ref="uploadElement" prop="img">
    <!--
    添加图片上传:
    设置:auto-upload="false" ; 关闭组件的默认自动上传
    action : 随便设置
    :on-change="handleChange" : 当组件的图片修改时,调用函数方法,来判断图片的格式大小,并替换图片
    :file-list="fileList" : 展示图片列表
    -->
    <el-upload
    class="upload-demo"
    action="#"
    accept="image/jpeg,image/jpg,image/png"
    ref="upload"
    :auto-upload="false"
    :on-change="handleChange"
    :file-list="fileList">
    <el-button size="small" type="primary">选择文件</el-button>
    <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过2MB</div>
    </el-upload>
    </el-form-item>
    <el-form-item>
    <el-select v-model="checkList" multiple placeholder="请选择">
    <el-option
    v-for="(category,index) in categorys"
    :key="index"
    :label="category.name"
    :value="category.id">
    </el-option>
    </el-select>
    </el-form-item>
    </el-form>
    <div slot="footer" class="dialog-footer">
    <el-button @click="dialogFormVisible = false">取 消</el-button>
    <el-button type="primary" @click="upload()">确 定</el-button>
    </div>
    </el-dialog>

js代码,函数方法

  • js代码:

  • 与html代码,配合一起使用。

  • 具有判断图片的格式 和 大小的方法。

  • 一次只能上传一个文件

    //图片方法
    //图片进行修改的方法
    handleChange(file, fileList) {
    let bo = this.beforeAvatarUpload(file);
    if(!bo){
    //图片不符合规范
    this.fileList = [];     //图片列表赋值为空
    return;
    }
    //如果图片符合 规范,将之前的图片剪切,覆盖掉
    this.fileList = fileList.slice(-1);
    },
    //图片判断,大小
    beforeAvatarUpload(file) {
    const isJPG = file.raw.type == 'image/jpeg';
    const isLt2M = file.size / 1024 / 1024 < 2;
    
    if (!isJPG) {
    this.$message.error('上传头像图片只能是 JPG 格式!');
    }
    if (!isLt2M) {
    this.$message.error('上传头像图片大小不能超过 2MB!');
    }
    return isJPG && isLt2M;
    },
    //添加方法
    async upload(){
    //将下拉框 选择的分类id,拼接成字符串,赋值给 addBrand对象中
    this.addBrand.categoryIds = this.checkList.toString();
    
    //上传图片 + 表单数据一起传给后台
    let fd = new FormData();
    //判断是否有文件
    if(this.fileList.length != 0){
    fd.append("file" , this.fileList[0].raw);
    }
    //添加格外的表单基本数据
    fd.append("name" , this.addBrand.name);
    fd.append("letter" , this.addBrand.letter);
    fd.append("categoryIds" , this.addBrand.categoryIds);
    
    //必须使用 post请求,因为是原生的 数据提交
    let {data} = await this.$ajax.post("/brand/add" , fd);
    
    this.findAll();
    this.dialogFormVisible = false;
    },

上传图片的原理解析

  • :on-change=“handleChange” : 在组件的图片改变时,会调用此函数方法,来判断图片的格式是否正确。
  • :file-list=“fileList” : 存储的文件列表,当前只上传一个文件,修改图片时,使用fileList.slice(-1);剪切掉之前的,只保留一个,存储在fileList属性中
  • 使用 new FormData(); 来存储各项数据 和 文件图片。发送post请求,访问后台

springboot后端代码

  • idea项目

添加上传文件所需要的 pom依赖

<!-- TODO 上传文件到阿里云 oss对象存储空间 -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.5.0</version>
</dependency>

<!--	TODO 	MultipartFile转File类型	DiskFileItem需要引入依赖-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>

controller方法

  • 使用普通的 formData表单提交,必须要 post请求

    @PostMapping("/add")
    public ResponseEntity<BaseResult> add(MultipartFile file , HttpServletRequest request , Brand brand) throws IOException {
    brandService.add(brand);
    
    BaseResult br = brandService.upload(request,file,brand);
    //将返回的 br中data 的url地址,赋值给 brand对象地址的 image中
    brand.setImage(String.valueOf(br.getData()));
    
    //将当前brand对象的 image属性修改
    //修改数据库中的 image字段
    brandService.updateImage(brand);
    
    //br为 图片上传的结果, data为上传上图片的url路径
    return ResponseEntity.ok(br);
    }

service方法

  • 在service方法中,调用 uploadUtils工具类,进行提交

  • 当前的提交效果为: 每个数据用户都拥有自己的一个 目录文件夹,来存储文件。 文件夹名称为 brand的id

    public BaseResult upload(HttpServletRequest request , MultipartFile file , Brand brand) throws IOException {
    //上传图片
    String url = "";
    
    //判断file是否为null,前端是否传过来了 图片
    if (file == null){
    return new BaseResult(CommonUtils.SUCCESS , "失败" , url);
    }
    
    try {
    //三个参数: file:文件 , request : 请求 , tableName : 目录名称,没有目录可以为"" or null
    UploadUtils.upload(request , file , "brand_"+brand.getId());
    url = UploadUtils.getURL(file.getOriginalFilename() , "brand_"+brand.getId());
    return new BaseResult(CommonUtils.SUCCESS , "成功" , url);
    } catch (Exception e) {
    e.printStackTrace();
    return new BaseResult(CommonUtils.SUCCESS , "失败" , url);
    }
    }

uploadUtils上传文件的工具类

  • 此工具类,可以实现,上传文件进度的滚动条获取

  • 该工具类 具有 上传,获取文件url地址,删除。等功能

  • 会自动将 MultipartFile 类型 转为 File类型

  • 可以添加 添加上传文件的目录

    /**
    * @author 遗憾就遗憾吧
    * @Date 2019/11/15
    * @jdk 1.8
    * 阿里云oss上传文件 工具类
    */
    public class UploadUtils {
    
    // Endpoint以杭州为例,其它Region请按实际情况填写。
    //endpoint 的 地址需要与你的 Bucket地址对应。 如果Bucket为shanghai,就设置为shanghai
    private static String endpoint = "http://oss-cn-shanghai.aliyuncs.com";
    //云账号AccessKey有所有API访问权限,建议遵循阿里云安全最佳实践,创建并使用RAM子账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建。
    private static String accessKeyId = "";
    private static String accessKeySecret = "";
    
    private static String bucketName = "2019-11-15";
    
    //滚动条值
    private static Integer percent = 0;
    
    /**
    * 用于外界赋值,目录名称。
    * 需求:每个品牌都拥有自己独立的一个 目录来存储文件
    */
    public static void upload(HttpServletRequest request , MultipartFile file , String tableName) throws IOException {
    System.out.println(file.getOriginalFilename());
    
    //TODO 进行赋值 文件的上传名称 包含目录 , 在赋值 tableName 中最后加上 / 隔开
    String objectName = "";
    if (tableName != null && !tableName.isEmpty()){
    objectName = tableName+"/"+file.getOriginalFilename();
    }else{
    objectName = file.getOriginalFilename();
    }
    
    //每次进入方法,就滚动条设置0
    setPercent(request);
    
    //将 MultipartFile 转为 File类型,进行上传到 oss
    File toFile = null;
    if (file.equals("") || file.getSize() <= 0) {
    file = null;
    } else {
    InputStream ins = null;
    ins = file.getInputStream();
    toFile = new File(file.getOriginalFilename());
    inputStreamToFile(ins, toFile);
    ins.close();
    }
    
    // 创建OSSClient实例。
    OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    
    try {
    // 带进度条的上传。
    PutObjectResult putObjectResult = ossClient.putObject(new PutObjectRequest(bucketName, objectName, toFile).
    <PutObjectRequest>withProgressListener(new PutObjectProgressListener(request)));
    } catch (Exception e) {
    e.printStackTrace();
    }
    
    // 关闭OSSClient。
    ossClient.shutdown();
    
    //最后删除掉,刚创建的 本地临时文件
    deleteTempFile(toFile);
    }
    
    /**
    * @param objectName  文件的名称
    * @param tableName  目录的名称
    * @return 获取上传文件的的 地址值
    */
    public static String getURL(String objectName , String tableName){
    // 创建OSSClient实例。
    OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    //通过 ossClient 可以获取 刚上传文件的 访问URL
    Date expiration = new Date(new Date().getTime() + 5 * 60 * 10000);
    
    //判断目录是否 为空 或者 null
    if (tableName != null && !tableName.isEmpty()){
    objectName = tableName+"/"+objectName;
    }
    
    URL url = ossClient.generatePresignedUrl(bucketName, objectName, expiration);
    
    System.out.println(url.toString());
    
    // 关闭OSSClient。
    ossClient.shutdown();
    return url.toString().split("\\?")[0];
    }
    
    /**
    * @param objectName 删除阿里云OSS上 存储的对应 fileName的文件
    * @param tableName 删除阿里云OSS上 存储的对应 fileName的目录
    */
    public static void delete(String objectName , String tableName){
    // <yourObjectName>表示删除OSS文件时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg。
    
    //判断目录是否 为空 或者 null
    if (tableName != null && !tableName.isEmpty()){
    objectName = tableName+"/"+objectName;
    }
    
    // 创建OSSClient实例。
    OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    
    // 删除文件。
    ossClient.deleteObject(bucketName, objectName);
    
    // 关闭OSSClient。
    ossClient.shutdown();
    }
    
    /**
    * @param request 获取当前滚动条的数值
    * @return
    */
    public static Integer getPercent(HttpServletRequest request){
    return UploadUtils.percent;
    }
    
    /**
    * @param request 将滚动条 重置0
    */
    private static void setPercent(HttpServletRequest request){
    //每次进入,upload上传图片方法,就赋值 upload_percent 为0 ; 重置长度
    //防止 GET请求获取 进度条值 upload_percent时,出现异步,先获取的问题
    //request.getSession().setAttribute("upload_percent" , 0);
    UploadUtils.percent = 0;
    }
    
    /**
    * 获取流文件,将流文件 赋值给 file
    * @param ins
    * @param file
    */
    private static void inputStreamToFile(InputStream ins, File file) {
    try {
    OutputStream os = new FileOutputStream(file);
    int bytesRead = 0;
    byte[] buffer = new byte[8192];
    while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
    os.write(buffer, 0, bytesRead);
    }
    os.close();
    ins.close();
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    
    /**
    * 删除本地临时文件
    * @param file
    */
    private static void deleteTempFile(File file) {
    if (file != null) {
    File del = new File(file.toURI());
    del.delete();
    }
    }
    
    /**
    *  上传图片的 滚动条进行 存储到 session中。
    *  当前存储在成员变量中。可能会产生某些问题
    */
    private static class PutObjectProgressListener implements ProgressListener {
    private long bytesWritten = 0;
    private long totalBytes = -1;
    private boolean succeed = false;
    private HttpServletRequest request;
    
    public PutObjectProgressListener(HttpServletRequest request) {
    this.request = request;
    }
    
    @Override
    public void progressChanged(ProgressEvent progressEvent) {
    long bytes = progressEvent.getBytes();
    ProgressEventType eventType = progressEvent.getEventType();
    switch (eventType) {
    case TRANSFER_STARTED_EVENT:
    System.out.println("Start to upload......");
    break;
    case REQUEST_CONTENT_LENGTH_EVENT:
    this.totalBytes = bytes;
    System.out.println(this.totalBytes + " bytes in total will be uploaded to OSS");
    break;
    case REQUEST_BYTE_TRANSFER_EVENT:
    this.bytesWritten += bytes;
    if (this.totalBytes != -1) {
    int percent = (int) (this.bytesWritten * 100.0 / this.totalBytes);
    System.out.println("我在赋值中"+percent);
    //                        request.getSession().setAttribute("upload_percent",percent);
    UploadUtils.percent = percent;
    System.out.println(bytes + " bytes have been written at this time, upload progress: " + percent + "%(" + this.bytesWritten + "/" + this.totalBytes + ")");
    } else {
    System.out.println(bytes + " bytes have been written at this time, upload ratio: unknown" + "(" + this.bytesWritten + "/...)");
    }
    break;
    case TRANSFER_COMPLETED_EVENT:
    this.succeed = true;
    System.out.println("Succeed to upload, " + this.bytesWritten + " bytes have been transferred in total");
    break;
    case TRANSFER_FAILED_EVENT:
    System.out.println("Failed to upload, " + this.bytesWritten + " bytes have been transferred");
    break;
    default:
    break;
    }
    }
    
    public boolean isSucceed() {
    return succeed;
    }
    }
    }

上传文件的带有滚动条效果

前端HTML代码:

  • 通过在js中设置一个 循环定时器,频繁的访问后台,获取后台的上传进度,返回给前台,在将进度值赋值进 进度条组件中。

    <template>
    <div>
    <el-upload
    class="upload-demo"
    action="http://localhost:8090/upload"
    :on-progress="getProgress"
    :file-list="fileList"
    list-type="picture"
    name="file"
    :with-credentials="true"
    style="width:30%">
    <el-button size="small" type="primary">点击上传</el-button>
    </el-upload>
    <el-progress v-if="isTure" :text-inside="true" :stroke-width="26" :percentage="percentage" :color="customColors" style="width: 30%;"></el-progress>
    </div>
    </template>
    
    <script>
    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'}],
    customColors: [
    {color: '#f56c6c', percentage: 20},
    {color: '#e6a23c', percentage: 40},
    {color: 'rgb(33, 187, 207)', percentage: 60},
    {color: '#1989fa', percentage: 80},
    {color: 'rgb(81, 184, 81)', percentage: 100}
    ],  //颜色展示
    isTure : false ,  //是否展示 滚动条
    percentage : 0 , //滚动条数据
    
    }
    },
    methods:{
    getProgress(event, file, fileList){
    //将进度条打开
    this.isTure = true;
    //此方法的开始,将percentage赋值0
    this.percentage = 0;
    
    //访问后台,获取滚动条数据
    //每隔 0.1秒访问一次后台,获取一次进度条数据
    let time = setInterval( () =>{
    //调用 获取进度条的方法
    this.getJD();
    
    if(this.percentage >= 100){
    clearInterval(time);
    this.isTure = false;
    this.percentage = 0;
    return;
    }
    } , 100);
    },
    async getJD(){
    let {data} = await this.$ajax.get("/upload/jd");
    console.log(data);
    this.percentage = data;
    }
    },
    }
    </script>
    
    <style>
    </style>

后端springboot代码

  • 在图片上传时。上传的进度会一直在后台,执行。并且返回有进度条。我们将进度条值,存储起来。

  • 然后在图片上传中时,我们可以进行其他的访问操作,在访问 设置定时器循环 频繁的访问后台,获取进度值。

  • 以下调用的方法,都是工具类 uploadUtils中的方法。

    @RequestMapping("/upload")
    @RestController
    public class UploadController {
    
    @PostMapping
    public ResponseEntity<String> upload(HttpServletRequest request , MultipartFile file) throws IOException {
    UploadUtils.upload(request , file , "");
    return ResponseEntity.ok("上传成功");
    }
    
    @GetMapping("/jd")
    public ResponseEntity<Integer> getJD(HttpServletRequest request) throws InterruptedException {
    //获取进度
    Integer percent = UploadUtils.getPercent(request);
    System.out.println("我被访问了,进度"+percent);
    return ResponseEntity.ok(percent);
    }
    
    @DeleteMapping("/delete")
    public ResponseEntity<String> delete(){
    UploadUtils.delete("强.jpg" , null);
    return ResponseEntity.ok("删除成功");
    }
    }

传成功");
}

@GetMapping("/jd")
public ResponseEntity<Integer> getJD(HttpServletRequest request) throws InterruptedException {
//获取进度
Integer percent = UploadUtils.getPercent(request);
System.out.println("我被访问了,进度"+percent);
return ResponseEntity.ok(percent);
}

@DeleteMapping("/delete")
public ResponseEntity<String> delete(){
UploadUtils.delete("强.jpg" , null);
return ResponseEntity.ok("删除成功");
}
}
  • 点赞
  • 收藏
  • 分享
  • 文章举报
一名努力的小码农 发布了15 篇原创文章 · 获赞 1 · 访问量 225 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: