Skip to content
索引

图片操作

上传图片流程

  1. 调用uni.chooseImage选择图片,可单选也可多选(多选最多九张),成功的话会返回图片的临时路径数组
  2. 上传至自行开发的后端接口使用uni.uploadFile,上传至unCloud的话则使用uniCloud.uploadFile
  3. 最佳写法是使用for循环来处理这个临时路径数组,这样无论是单选还是多选,都可以正确上传
  4. 上传成功后会返回图片的真实路径,这时应当存储真实路径至表中
  5. 上传文件至unCloud的话,成功后图片会存储在云存储中
  6. 删除图片,不仅要删除数据库表中的数据,还要删除云存储中的文件

uni.chooseImage选择图片

  • 最多选择九张,超过九张,只有前九张会在图片的临时路径数组中返回
  • 成功的回调对象包含tempFilePathstempFiles两个数组属性
  • tempFilePaths数组的元素,是tempFiles数组的所有path属性值
  • 从基础库 2.21.0 开始,wx.chooseImage停止维护,请使用 wx.chooseMedia 代替,uniapp中使用uni.chooseMedia代替
js
uni.chooseImage({
    success: (res) => {
        console.log(res)
        for (let item of res.tempFilePaths) {
        }
        for (let item of res.tempFiles) {
        }
    }
})
uni.chooseImage({
    success: (res) => {
        console.log(res)
        for (let item of res.tempFilePaths) {
        }
        for (let item of res.tempFiles) {
        }
    }
})

单张图片上传

js
uni.chooseImage({
    success: (res) => {
        const tempFilePaths = res.tempFilePaths;
        uni.uploadFile({
            url: 'https://www.example.com/upload', //仅为示例,非真实的接口地址
            filePath: tempFilePaths[0],
            name: 'file',
            formData: {
                'user': 'test'
            },
            success: (uploadFileRes) => {
                console.log(uploadFileRes.data)
            }
        })
    }
})
uni.chooseImage({
    success: (res) => {
        const tempFilePaths = res.tempFilePaths;
        uni.uploadFile({
            url: 'https://www.example.com/upload', //仅为示例,非真实的接口地址
            filePath: tempFilePaths[0],
            name: 'file',
            formData: {
                'user': 'test'
            },
            success: (uploadFileRes) => {
                console.log(uploadFileRes.data)
            }
        })
    }
})

多张图片上传使用循环

js
uni.chooseImage({
    success: (res) => {
        const tempFilePaths = res.tempFilePaths;
        for(let i = 0; i < tempFilePaths.length; i++) {
        	uni.uploadFile({
	            url: 'https://www.example.com/upload', //仅为示例,非真实的接口地址
	            filePath: tempFilePaths[i],
	            name: 'file',
	            formData: {
	                'user': 'test'
	            },
	            success: (uploadFileRes) => {
	                console.log(uploadFileRes.data)
	            }
	        })
        }
    }
})
uni.chooseImage({
    success: (res) => {
        const tempFilePaths = res.tempFilePaths;
        for(let i = 0; i < tempFilePaths.length; i++) {
        	uni.uploadFile({
	            url: 'https://www.example.com/upload', //仅为示例,非真实的接口地址
	            filePath: tempFilePaths[i],
	            name: 'file',
	            formData: {
	                'user': 'test'
	            },
	            success: (uploadFileRes) => {
	                console.log(uploadFileRes.data)
	            }
	        })
        }
    }
})

如果是app或者h5端,可以设置files实现多张图片上传

js
uni.chooseImage({
    success: (chooseImageRes) => {
        const tempFilePaths = chooseImageRes.tempFilePaths;
        let imgs = tempFilePaths.map((value, index) => {
        	return {
        	name: index,
        	uri: value
        })
        uni.uploadFile({
            url: 'https://www.example.com/upload', //仅为示例,非真实的接口地址
            files: imgs,
            success: (uploadFileRes) => {
                console.log(uploadFileRes.data);
            }
        });
    }
});
uni.chooseImage({
    success: (chooseImageRes) => {
        const tempFilePaths = chooseImageRes.tempFilePaths;
        let imgs = tempFilePaths.map((value, index) => {
        	return {
        	name: index,
        	uri: value
        })
        uni.uploadFile({
            url: 'https://www.example.com/upload', //仅为示例,非真实的接口地址
            files: imgs,
            success: (uploadFileRes) => {
                console.log(uploadFileRes.data);
            }
        });
    }
});

预览图片

js
uni.previewImage({
			urls: res.tempFilePaths,
			longPressActions: {
				itemList: ['发送给朋友', '保存图片', '收藏'],
				success: function(data) {
					console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) + '张图片');
				},
				fail: function(err) {
					console.log(err.errMsg);
				}
			}
		});
uni.previewImage({
			urls: res.tempFilePaths,
			longPressActions: {
				itemList: ['发送给朋友', '保存图片', '收藏'],
				success: function(data) {
					console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) + '张图片');
				},
				fail: function(err) {
					console.log(err.errMsg);
				}
			}
		});

unicloud云函数批量上传图片

js
upload() {
      // 选择图片,默认不设置count属性的话最多9张
      uni.chooseImage({
        success(res) {
          // 通过遍历调用uniCloud.uploadFile来批量上传
          for (let i = 0; i < res.tempFilePaths.length; i++) {
            uni.showLoading({
              title: "上传中..."
            })
            let filePath = res.tempFilePaths[i]
            uniCloud.uploadFile({
              filePath, // 必传,要上传的文件对象
              cloudPath: Date.now() + "-img", // 必传,保存在云端的文件名,这里以时间戳命名
              success(res) {
                let imageUrl = res.fileID //云端返回的图片地址
                // 调用云函数保存图片数据
                uniCloud.callFunction({
                  name: "addImage", 
                  // 传给云函数的数据,可根据自身需求进行改动
                  data:{
                      imageUrl: imageUrl, // 图片路径
                      createTime: Date.now() // 创建时间
                  },
                  success: res => {
                    console.log(res)
                  },
                  fail: err => {
                    console.log(err)
                  },
                  complete: () => {}
                })
              },
              fail(err) {
                console.log(err)
              },
              complete() {
                uni.hideLoading()
              }
            })
          }
        }
      })
    },
upload() {
      // 选择图片,默认不设置count属性的话最多9张
      uni.chooseImage({
        success(res) {
          // 通过遍历调用uniCloud.uploadFile来批量上传
          for (let i = 0; i < res.tempFilePaths.length; i++) {
            uni.showLoading({
              title: "上传中..."
            })
            let filePath = res.tempFilePaths[i]
            uniCloud.uploadFile({
              filePath, // 必传,要上传的文件对象
              cloudPath: Date.now() + "-img", // 必传,保存在云端的文件名,这里以时间戳命名
              success(res) {
                let imageUrl = res.fileID //云端返回的图片地址
                // 调用云函数保存图片数据
                uniCloud.callFunction({
                  name: "addImage", 
                  // 传给云函数的数据,可根据自身需求进行改动
                  data:{
                      imageUrl: imageUrl, // 图片路径
                      createTime: Date.now() // 创建时间
                  },
                  success: res => {
                    console.log(res)
                  },
                  fail: err => {
                    console.log(err)
                  },
                  complete: () => {}
                })
              },
              fail(err) {
                console.log(err)
              },
              complete() {
                uni.hideLoading()
              }
            })
          }
        }
      })
    },

云函数addImage

需要创建一个tableImages表

js
'use strict';
const db = uniCloud.database()
exports.main = async (event, context) => {
  const collection = db.collection('tableImages') // 云数据库里的表名 ,记录上传图片到云存储后返回的fileID
  const res = await collection.add(event) // event为客户端上传的参数
  return res
};
'use strict';
const db = uniCloud.database()
exports.main = async (event, context) => {
  const collection = db.collection('tableImages') // 云数据库里的表名 ,记录上传图片到云存储后返回的fileID
  const res = await collection.add(event) // event为客户端上传的参数
  return res
};

uncloud-h5端上传图片跨域

需要在unicloud-web控制台>跨域配置>新增域名>设置安全域名

比如我前端h5地址是http://172.16.0.152:8081/#/

则安全域名设置为172.16.0.152:8081

unicloud删除图片

思路:除了删除表中的对应数据,还得删除云存储上的图片文件

创建云函数deleteImage

js
'use strict';

const db = uniCloud.database()
exports.main = async (event, context) => {
  const collection = db.collection('tableImages') // 根据表名获取云数据库里的表
  // 同时执行删除文件和删除表中数据的操作
  const res = await Promise.all([uniCloud.deleteFile({
    fileList: [
      event.fileId
    ]
  }), collection.doc(event.id).remove()])
  // 返回删除文件和删除表中数据的操作结果
  return {
    deleteFile: res[0],
    deleleData: res[1]
  }
}
'use strict';

const db = uniCloud.database()
exports.main = async (event, context) => {
  const collection = db.collection('tableImages') // 根据表名获取云数据库里的表
  // 同时执行删除文件和删除表中数据的操作
  const res = await Promise.all([uniCloud.deleteFile({
    fileList: [
      event.fileId
    ]
  }), collection.doc(event.id).remove()])
  // 返回删除文件和删除表中数据的操作结果
  return {
    deleteFile: res[0],
    deleleData: res[1]
  }
}

多文件上传示例

vue
<template>
  <view class="page">
    <view class="address-box add-box">
      <text class="label label-img">多图片上传示例</text>
      <view class="add-img-box">
        <view class="add-img-item" v-for="(item, index) in imgList" :key="index">
          <image class="add-img" @click="imgInfo(index)" :src="item.path" mode="aspectFill"></image>
          <image class="add-img-del" @click="delImg(index)" src="../../static/delete.png"></image>
        </view>
        <view v-if="imgList.length < 9" class="add-img-item" @click="openCamera">
          <image class="add-img" src="../../static/add.png"></image>
        </view>
      </view>
      <text class="label label-img">服务器图片</text>
      <view class="add-img-box">
        <view class="add-img-item" v-for="(item, index) in cloudimgList" :key="index">
          <image class="add-img" :src="item" mode="aspectFill"></image>
          <image
            class="add-img-del"
            @click="delcloudImg(index)"
            src="../../static/delete.png"
          ></image>
        </view>
      </view>
    </view>
    <view @click="submit" class="address-box submit-box">
      <text class="submit-btn">上传服务器</text>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      imgList: [],
      cloudimgList: [],
      imgCount: 9 //最多支持9张上传,可以修改
    }
  },
  onLoad(e) {},
  methods: {
    submit(e) {
      this.submitData = null
      if (this.imgList.length < 1) {
        uni.showToast({
          title: "请添加图片",
          icon: "none"
        })
        return false
      }
      uni.showLoading({
        title: "上传图片",
        mask: false
      })
      for (let i = 0; i < this.imgList.length; i++) {
        console.log("test!" + this.imgList[i].path)
        this.$cloud.uploadFile({
          filePath: this.imgList[i].path,
          cloudPath: Date.now() + "-img", //保存在云端的文件名,这里以时间戳命名
          success: res => {
            //保存图片的路径
            this.cloudimgList.push(res.fileID)
          },
          fail: err => {
            console.log(err)
          },
          complete: () => {
            //uni.hideLoading();
            if (this.cloudimgList.length == this.imgList.length) {
              uni.hideLoading()
              uni.showToast({
                title: "全部上传成功!",
                icon: "none"
              })
            }
          }
        })
      }
    },

    imgInfo(i) {
      let tempList = []
      this.imgList.forEach(img => {
        tempList.push(img.path)
      })
      console.log(tempList)
      //显示图片
      uni.previewImage({
        current: i,
        loop: false,
        urls: tempList,
        indicator: "default"
      })
    },
    delImg(i) {
      uni.showModal({
        content: "确定删除这张吗",
        success: res => {
          if (res.confirm) {
            this.imgList.splice(i, 1)
            this.imgCount++
          } else if (res.cancel) {
          }
        }
      })
    },
    delcloudImg(i) {
      var _self = this
      uni.showModal({
        content: "确定删除云服务器上面这张图吗",
        success: res => {
          if (res.confirm) {
            this.$cloud.deleteFile({
              fileList: [this.cloudimgList[i]],
              success(res) {},
              fail(err) {
                console.log(err)
              },
              complete(res) {
                uni.showToast({
                  title: "删除成功!",
                  icon: "none"
                })
                _self.cloudimgList.splice(i, 1)
              }
            })
          } else if (res.cancel) {
          }
        }
      })
    },
    openCamera() {
      uni.chooseImage({
        sourceType: ["camera"],
        count: this.imgCount,
        sizeType: ["compressed"],
        success: e => {
          this.imgList = [...this.imgList, ...e.tempFiles]

          this.imgCount = 9 - this.imgList.length
          console.log("imgCount", this.imgCount)
        }
      })
    }
  }
}
</script>

<style lang="scss">
.page {
  width: 750rpx;
  overflow: hidden;
}
.back {
  font-size: 32rpx;
  font-weight: 500;
  line-height: 48rpx;
  color: rgba(153, 153, 153, 1);
}
.back-hover {
  opacity: 0.4;
}
.submit-box {
  position: fixed;
  bottom: 0;
  left: 0;
  display: flex;
  width: 750rpx;
  justify-content: center;
}
.submit-btn {
  margin-top: 10px;
  display: inline-block;
  width: 670rpx;
  height: 96rpx;
  line-height: 96rpx;
  text-align: center;
  background-color: rgba(109, 221, 203, 1);
  opacity: 1;
  border-radius: 32rpx;
  border-width: 8rpx;
  border-color: rgba(255, 255, 255, 1);
  box-shadow: 0rpx 8rpx 12rpx rgba(0, 0, 0, 0.16);
  border-radius: 56rpx;
  font-size: 39rpx;
  font-weight: bold;
  color: rgba(255, 255, 255, 1);
}
.submit-btn-txt {
  font-size: 39rpx;
  font-weight: bold;
  line-height: 47rpx;
  color: rgba(255, 255, 255, 1);
  opacity: 1;
}
.map-box {
  width: 484rpx;
  height: 256rpx;
  border-width: 4rpx;
  border-color: rgba(255, 255, 255, 1);
  box-shadow: 0rpx 0rpx 24rpx rgba(0, 0, 0, 0.16);
  /* border-radius: 12rpx; */
  position: relative;
}
.map {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 476rpx;
  height: 250rpx;
}
.map-img {
  position: absolute;
  top: 90rpx;
  left: 156rpx;
  width: 230rpx;
  height: 68rpx;
  background-color: rgba(51, 51, 51, 0.64);
  border-width: 1rpx;
  border-color: rgba(0, 0, 0, 0);
  border-radius: 34px;

  font-size: 28rpx;
  font-weight: bold;
  line-height: 66rpx;
  color: rgba(255, 255, 255, 1);
  text-align: center;
}
.address-box {
  display: flex;
  width: 670rpx;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  padding: 15rpx 40rpx;
  margin-bottom: 10px;
}
.label {
  font-size: 36rpx;
  font-weight: bold;
  line-height: 50rpx;
  color: #222222;
}
.label-img {
  padding-left: 40rpx;
}
.address-box-txt {
  align-items: flex-start;
}
.address-txt {
  width: 484rpx;
  height: 90rpx;
  font-size: 32rpx;
  font-weight: 500;
  line-height: 45rpx;
  color: rgba(51, 51, 51, 1);
}
.add-box {
  padding: 15rpx 0;
  flex-wrap: wrap;
}
.add-img-box {
  display: flex;
  width: 750rpx;
  //padding: 0 40rpx;
  padding-left: 10rpx;
  flex-direction: row;
  flex-wrap: wrap;
}
.add-img-item {
  /* width:210rpx;
		height:210rpx; */
  width: 200rpx;
  height: 200rpx;
  border-radius: 24rpx;
  position: relative;
  padding: 9rpx 0;
  margin-left: 20rpx;
}
.add-img-camera {
  flex: 1;
}
.add-img {
  width: 200rpx;
  height: 200rpx;
  border-radius: 24rpx;
}
.add-img-del {
  position: absolute;
  width: 40rpx;
  height: 40rpx;
  right: 8rpx;
  top: 17rpx;
  //background-color: rgba(238, 0, 0, 1);
  border-radius: 20rpx;
}
.address-time {
  width: 484rpx;
  height: 88rpx;
  background-color: rgba(245, 245, 245, 1);
  opacity: 1;
  border-radius: 24rpx;
  text-align: center;

  font-size: 35rpx;
  font-weight: 500;
  color: rgba(51, 51, 51, 1);
}
.line {
  width: 750rpx;
  height: 1px;
  transform: scaleY(0.3);
  background-color: rgba(0, 0, 0, 0.5);
}
</style>
<template>
  <view class="page">
    <view class="address-box add-box">
      <text class="label label-img">多图片上传示例</text>
      <view class="add-img-box">
        <view class="add-img-item" v-for="(item, index) in imgList" :key="index">
          <image class="add-img" @click="imgInfo(index)" :src="item.path" mode="aspectFill"></image>
          <image class="add-img-del" @click="delImg(index)" src="../../static/delete.png"></image>
        </view>
        <view v-if="imgList.length < 9" class="add-img-item" @click="openCamera">
          <image class="add-img" src="../../static/add.png"></image>
        </view>
      </view>
      <text class="label label-img">服务器图片</text>
      <view class="add-img-box">
        <view class="add-img-item" v-for="(item, index) in cloudimgList" :key="index">
          <image class="add-img" :src="item" mode="aspectFill"></image>
          <image
            class="add-img-del"
            @click="delcloudImg(index)"
            src="../../static/delete.png"
          ></image>
        </view>
      </view>
    </view>
    <view @click="submit" class="address-box submit-box">
      <text class="submit-btn">上传服务器</text>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      imgList: [],
      cloudimgList: [],
      imgCount: 9 //最多支持9张上传,可以修改
    }
  },
  onLoad(e) {},
  methods: {
    submit(e) {
      this.submitData = null
      if (this.imgList.length < 1) {
        uni.showToast({
          title: "请添加图片",
          icon: "none"
        })
        return false
      }
      uni.showLoading({
        title: "上传图片",
        mask: false
      })
      for (let i = 0; i < this.imgList.length; i++) {
        console.log("test!" + this.imgList[i].path)
        this.$cloud.uploadFile({
          filePath: this.imgList[i].path,
          cloudPath: Date.now() + "-img", //保存在云端的文件名,这里以时间戳命名
          success: res => {
            //保存图片的路径
            this.cloudimgList.push(res.fileID)
          },
          fail: err => {
            console.log(err)
          },
          complete: () => {
            //uni.hideLoading();
            if (this.cloudimgList.length == this.imgList.length) {
              uni.hideLoading()
              uni.showToast({
                title: "全部上传成功!",
                icon: "none"
              })
            }
          }
        })
      }
    },

    imgInfo(i) {
      let tempList = []
      this.imgList.forEach(img => {
        tempList.push(img.path)
      })
      console.log(tempList)
      //显示图片
      uni.previewImage({
        current: i,
        loop: false,
        urls: tempList,
        indicator: "default"
      })
    },
    delImg(i) {
      uni.showModal({
        content: "确定删除这张吗",
        success: res => {
          if (res.confirm) {
            this.imgList.splice(i, 1)
            this.imgCount++
          } else if (res.cancel) {
          }
        }
      })
    },
    delcloudImg(i) {
      var _self = this
      uni.showModal({
        content: "确定删除云服务器上面这张图吗",
        success: res => {
          if (res.confirm) {
            this.$cloud.deleteFile({
              fileList: [this.cloudimgList[i]],
              success(res) {},
              fail(err) {
                console.log(err)
              },
              complete(res) {
                uni.showToast({
                  title: "删除成功!",
                  icon: "none"
                })
                _self.cloudimgList.splice(i, 1)
              }
            })
          } else if (res.cancel) {
          }
        }
      })
    },
    openCamera() {
      uni.chooseImage({
        sourceType: ["camera"],
        count: this.imgCount,
        sizeType: ["compressed"],
        success: e => {
          this.imgList = [...this.imgList, ...e.tempFiles]

          this.imgCount = 9 - this.imgList.length
          console.log("imgCount", this.imgCount)
        }
      })
    }
  }
}
</script>

<style lang="scss">
.page {
  width: 750rpx;
  overflow: hidden;
}
.back {
  font-size: 32rpx;
  font-weight: 500;
  line-height: 48rpx;
  color: rgba(153, 153, 153, 1);
}
.back-hover {
  opacity: 0.4;
}
.submit-box {
  position: fixed;
  bottom: 0;
  left: 0;
  display: flex;
  width: 750rpx;
  justify-content: center;
}
.submit-btn {
  margin-top: 10px;
  display: inline-block;
  width: 670rpx;
  height: 96rpx;
  line-height: 96rpx;
  text-align: center;
  background-color: rgba(109, 221, 203, 1);
  opacity: 1;
  border-radius: 32rpx;
  border-width: 8rpx;
  border-color: rgba(255, 255, 255, 1);
  box-shadow: 0rpx 8rpx 12rpx rgba(0, 0, 0, 0.16);
  border-radius: 56rpx;
  font-size: 39rpx;
  font-weight: bold;
  color: rgba(255, 255, 255, 1);
}
.submit-btn-txt {
  font-size: 39rpx;
  font-weight: bold;
  line-height: 47rpx;
  color: rgba(255, 255, 255, 1);
  opacity: 1;
}
.map-box {
  width: 484rpx;
  height: 256rpx;
  border-width: 4rpx;
  border-color: rgba(255, 255, 255, 1);
  box-shadow: 0rpx 0rpx 24rpx rgba(0, 0, 0, 0.16);
  /* border-radius: 12rpx; */
  position: relative;
}
.map {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 476rpx;
  height: 250rpx;
}
.map-img {
  position: absolute;
  top: 90rpx;
  left: 156rpx;
  width: 230rpx;
  height: 68rpx;
  background-color: rgba(51, 51, 51, 0.64);
  border-width: 1rpx;
  border-color: rgba(0, 0, 0, 0);
  border-radius: 34px;

  font-size: 28rpx;
  font-weight: bold;
  line-height: 66rpx;
  color: rgba(255, 255, 255, 1);
  text-align: center;
}
.address-box {
  display: flex;
  width: 670rpx;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  padding: 15rpx 40rpx;
  margin-bottom: 10px;
}
.label {
  font-size: 36rpx;
  font-weight: bold;
  line-height: 50rpx;
  color: #222222;
}
.label-img {
  padding-left: 40rpx;
}
.address-box-txt {
  align-items: flex-start;
}
.address-txt {
  width: 484rpx;
  height: 90rpx;
  font-size: 32rpx;
  font-weight: 500;
  line-height: 45rpx;
  color: rgba(51, 51, 51, 1);
}
.add-box {
  padding: 15rpx 0;
  flex-wrap: wrap;
}
.add-img-box {
  display: flex;
  width: 750rpx;
  //padding: 0 40rpx;
  padding-left: 10rpx;
  flex-direction: row;
  flex-wrap: wrap;
}
.add-img-item {
  /* width:210rpx;
		height:210rpx; */
  width: 200rpx;
  height: 200rpx;
  border-radius: 24rpx;
  position: relative;
  padding: 9rpx 0;
  margin-left: 20rpx;
}
.add-img-camera {
  flex: 1;
}
.add-img {
  width: 200rpx;
  height: 200rpx;
  border-radius: 24rpx;
}
.add-img-del {
  position: absolute;
  width: 40rpx;
  height: 40rpx;
  right: 8rpx;
  top: 17rpx;
  //background-color: rgba(238, 0, 0, 1);
  border-radius: 20rpx;
}
.address-time {
  width: 484rpx;
  height: 88rpx;
  background-color: rgba(245, 245, 245, 1);
  opacity: 1;
  border-radius: 24rpx;
  text-align: center;

  font-size: 35rpx;
  font-weight: 500;
  color: rgba(51, 51, 51, 1);
}
.line {
  width: 750rpx;
  height: 1px;
  transform: scaleY(0.3);
  background-color: rgba(0, 0, 0, 0.5);
}
</style>

下载图片至手机相册

提供线上地址

js
<template>
  <div>
    <img src="https://img-blog.csdnimg.cn/a5afbc54539a4bc79125c6ebf36ba480.png" @click="exportPDF" />
  </div>
</template>

<script>
export default {
  methods: {
    exportPDF() {
      const Url = "https://img-blog.csdnimg.cn/a5afbc54539a4bc79125c6ebf36ba480.png"
      const a = document.createElement("a")
      a.href = Url
      a.download = "download"
      a.click()
    }
  }
}
</script>
<template>
  <div>
    <img src="https://img-blog.csdnimg.cn/a5afbc54539a4bc79125c6ebf36ba480.png" @click="exportPDF" />
  </div>
</template>

<script>
export default {
  methods: {
    exportPDF() {
      const Url = "https://img-blog.csdnimg.cn/a5afbc54539a4bc79125c6ebf36ba480.png"
      const a = document.createElement("a")
      a.href = Url
      a.download = "download"
      a.click()
    }
  }
}
</script>

Released under the MIT License.