11.1.2 vue-element-admin 上传图片慢问题处理

前言

vue-element-admin自带上传图片组件,在使用的过程中发现上传速度很慢,尤其是上传一些大图需要耗时几十秒不能忍受。出现这种情况,是因为upload组件会将图片上传到action="https://httpbin.org/post" ,并返回转换成base64编码格式的数据。

格式类似:

  1. data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAIBAQEBAQIBAQECAgICAgQDAgICAgUEBAMEBgUGBgYFBgYGBwkIBgcJBwYGCAsIC.....

而且有可能这个base64编码比上传源文件还要大。

这样做有两个缺点:

还有一点就这种是必须图片和表单其他内容一起提交,有的时候上传和表单其他项分开提交。

接下来讲一下如何将图片单独上传到服务的实现步骤:

具体代码在以下项目里

github:

https://github.com/guyan0319/go-admin

码云(国内):

https://gitee.com/jason0319/go-admin

示例一、解决如下图所示的上传和删除

vue-element-admin 上传图片慢问题处理 - 图1

1、新建文件

  1. vue-element-admin\src\utils\global.js

内容如下

  1. const httphost = 'http://localhost:8090'
  2. export { httphost }

这个是服务端的地址,可以根据需要自己调整。

2、修改上传组件代码在SingleImage3.vue

上传图片

引入上面定义的服务器地址

  1. import { httphost } from '@/utils/global'

data()增加uploadUrl

  1. data() {
  2. return {
  3. tempUrl: '',
  4. uploadUrl: httphost + '/upload/image',
  5. dataObj: { token: '', key: '' }
  6. }
  7. },

  1. action=``"https://httpbin.org/post"

修改为

  1. :action="uploadUrl"

图片上传成功on-success绑定的handleImageSuccess函数增加了res,即服务端返回上传结果。

修改代码如下

  1. handleImageSuccess(res, file) {
  2. if (res.code !== 20000){
  3. this.$message({
  4. message: '上传失败',
  5. type: 'error',
  6. showClose: true
  7. })
  8. return false
  9. }
  10. this.emitInput(res.data)
  11. },

服务端返回的json格式为

  1. {
  2. "code":20000,
  3. "data":"http://localhost:8090/showimage?imgname=upload/20200620/tX5vS810l2Fl0K02I0YJLEjLEw9OH7hc.jpg"
  4. }

这里需要注意el-upload增加

  1. :with-credentials='true'

支持发送 cookie 凭证信息,上传文件到服务器端需要判断验证登录。

删除图片

通过以上修改实现上传图片,接下处理上传文件删除

文件api/article.js 增加

  1. export function delImage(url) {
  2. return request({
  3. url: '',
  4. method: 'get',
  5. params: { url },
  6. baseURL: httphost + '/del/image'
  7. })
  8. }

修改SingleImage3.vue

  1. //引入delImage
  2. import { delImage } from '@/api/article'
  1. rmImage() {
  2. delImage(this.value).then(response => {
  3. if (response.code === 20000){
  4. this.emitInput('')
  5. return
  6. }
  7. this.$message({
  8. message: '删除失败',
  9. type: 'error',
  10. showClose: true
  11. })
  12. }).catch(err => {
  13. console.log(err)
  14. })
  15. },

服务端删除文件返回json

  1. {"code":20000,"data":"success"}

最后贴一下SingleImage3.vue修改后完整的代码

  1. <template>
  2. <div class="upload-container">
  3. <el-upload
  4. :data="dataObj"
  5. :multiple="false"
  6. :show-file-list="false"
  7. :with-credentials='true'
  8. :on-success="handleImageSuccess"
  9. class="image-uploader"
  10. drag
  11. :action="uploadUrl"
  12. >
  13. <i class="el-icon-upload" />
  14. <div class="el-upload__text">
  15. 将文件拖到此处,或<em>点击上传</em>
  16. </div>
  17. </el-upload>
  18. <div class="image-preview image-app-preview">
  19. <div v-show="imageUrl.length>1" class="image-preview-wrapper">
  20. <img :src="imageUrl">
  21. <div class="image-preview-action">
  22. <i class="el-icon-delete" @click="rmImage" />
  23. </div>
  24. </div>
  25. </div>
  26. <div class="image-preview">
  27. <div v-show="imageUrl.length>1" class="image-preview-wrapper">
  28. <img :src="imageUrl">
  29. <div class="image-preview-action">
  30. <i class="el-icon-delete" @click="rmImage" />
  31. </div>
  32. </div>
  33. </div>
  34. </div>
  35. </template>
  36. <script>
  37. import { getToken } from '@/api/qiniu'
  38. import { delImage } from '@/api/article'
  39. import { httphost } from '@/utils/global'
  40. // import { Cookies } from 'js-cookie'
  41. export default {
  42. name: 'SingleImageUpload3',
  43. props: {
  44. value: {
  45. type: String,
  46. default: ''
  47. }
  48. },
  49. data() {
  50. return {
  51. tempUrl: '',
  52. uploadUrl: httphost + '/upload/image',
  53. dataObj: { token: '', key: '' }
  54. }
  55. },
  56. computed: {
  57. imageUrl() {
  58. return this.value
  59. }
  60. },
  61. methods: {
  62. rmImage() {
  63. delImage(this.value).then(response => {
  64. if (response.code === 20000){
  65. this.emitInput('')
  66. return
  67. }
  68. this.$message({
  69. message: '删除失败',
  70. type: 'error',
  71. showClose: true
  72. })
  73. }).catch(err => {
  74. console.log(err)
  75. })
  76. },
  77. emitInput(val) {
  78. this.$emit('input', val)
  79. },
  80. handleImageSuccess(res, file) {
  81. if (res.code !== 20000){
  82. this.$message({
  83. message: '上传失败',
  84. type: 'error',
  85. showClose: true
  86. })
  87. return false
  88. }
  89. this.emitInput(res.data)
  90. },
  91. beforeUpload() {
  92. const _self = this
  93. return new Promise((resolve, reject) => {
  94. getToken().then(response => {
  95. const key = response.data.qiniu_key
  96. const token = response.data.qiniu_token
  97. _self._data.dataObj.token = token
  98. _self._data.dataObj.key = key
  99. this.tempUrl = response.data.qiniu_url
  100. resolve(true)
  101. }).catch(err => {
  102. console.log(err)
  103. reject(false)
  104. })
  105. })
  106. }
  107. }
  108. }
  109. </script>
  110. <style lang="scss" scoped>
  111. @import "~@/styles/mixin.scss";
  112. .upload-container {
  113. width: 100%;
  114. position: relative;
  115. @include clearfix;
  116. .image-uploader {
  117. width: 35%;
  118. float: left;
  119. }
  120. .image-preview {
  121. width: 200px;
  122. height: 200px;
  123. position: relative;
  124. border: 1px dashed #d9d9d9;
  125. float: left;
  126. margin-left: 50px;
  127. .image-preview-wrapper {
  128. position: relative;
  129. width: 100%;
  130. height: 100%;
  131. img {
  132. width: 100%;
  133. height: 100%;
  134. }
  135. }
  136. .image-preview-action {
  137. position: absolute;
  138. width: 100%;
  139. height: 100%;
  140. left: 0;
  141. top: 0;
  142. cursor: default;
  143. text-align: center;
  144. color: #fff;
  145. opacity: 0;
  146. font-size: 20px;
  147. background-color: rgba(0, 0, 0, .5);
  148. transition: opacity .3s;
  149. cursor: pointer;
  150. text-align: center;
  151. line-height: 200px;
  152. .el-icon-delete {
  153. font-size: 36px;
  154. }
  155. }
  156. &:hover {
  157. .image-preview-action {
  158. opacity: 1;
  159. }
  160. }
  161. }
  162. .image-app-preview {
  163. width: 320px;
  164. height: 180px;
  165. position: relative;
  166. border: 1px dashed #d9d9d9;
  167. float: left;
  168. margin-left: 50px;
  169. .app-fake-conver {
  170. height: 44px;
  171. position: absolute;
  172. width: 100%; // background: rgba(0, 0, 0, .1);
  173. text-align: center;
  174. line-height: 64px;
  175. color: #fff;
  176. }
  177. }
  178. }
  179. </style>

示例二、解决如下图所示的上传

vue-element-admin 上传图片慢问题处理 - 图2

需要修改vue-element-admin\src\components\Tinymce\components\EditorImage.vue文件,处理方式和示例一差不多,这里只贴代码

  1. <template>
  2. <div class="upload-container">
  3. <el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true">
  4. upload
  5. </el-button>
  6. <el-dialog :visible.sync="dialogVisible">
  7. <el-upload
  8. :multiple="true"
  9. :file-list="fileList"
  10. :show-file-list="true"
  11. :with-credentials='true'
  12. :on-remove="handleRemove"
  13. :on-success="handleSuccess"
  14. :before-upload="beforeUpload"
  15. class="editor-slide-upload"
  16. :action="uploadUrl"
  17. list-type="picture-card"
  18. >
  19. <el-button size="small" type="primary">
  20. Click upload
  21. </el-button>
  22. </el-upload>
  23. <el-button @click="dialogVisible = false">
  24. Cancel
  25. </el-button>
  26. <el-button type="primary" @click="handleSubmit">
  27. Confirm
  28. </el-button>
  29. </el-dialog>
  30. </div>
  31. </template>
  32. <script>
  33. // import { getToken } from 'api/qiniu'
  34. import { delImage } from '@/api/article'
  35. import { httphost } from '@/utils/global'
  36. export default {
  37. name: 'EditorSlideUpload',
  38. props: {
  39. color: {
  40. type: String,
  41. default: '#1890ff'
  42. }
  43. },
  44. data() {
  45. return {
  46. dialogVisible: false,
  47. uploadUrl: httphost + '/upload/image',
  48. listObj: {},
  49. fileList: []
  50. }
  51. },
  52. methods: {
  53. checkAllSuccess() {
  54. return Object.keys(this.listObj).every(item => this.listObj[item].hasSuccess)
  55. },
  56. handleSubmit() {
  57. const arr = Object.keys(this.listObj).map(v => this.listObj[v])
  58. if (!this.checkAllSuccess()) {
  59. this.$message('Please wait for all images to be uploaded successfully. If there is a network problem, please refresh the page and upload again!')
  60. return
  61. }
  62. this.$emit('successCBK', arr)
  63. this.listObj = {}
  64. this.fileList = []
  65. this.dialogVisible = false
  66. },
  67. handleSuccess(response, file) {
  68. const uid = file.uid
  69. const objKeyArr = Object.keys(this.listObj)
  70. for (let i = 0, len = objKeyArr.length; i < len; i++) {
  71. if (this.listObj[objKeyArr[i]].uid === uid) {
  72. this.listObj[objKeyArr[i]].url = response.data
  73. // this.listObj[objKeyArr[i]].url = response.files.file
  74. this.listObj[objKeyArr[i]].hasSuccess = true
  75. return
  76. }
  77. }
  78. },
  79. handleRemove(file) {
  80. const uid = file.uid
  81. const objKeyArr = Object.keys(this.listObj)
  82. for (let i = 0, len = objKeyArr.length; i < len; i++) {
  83. if (this.listObj[objKeyArr[i]].uid === uid) {
  84. delImage(this.listObj[objKeyArr[i]].url).then(response => {
  85. if (response.code !== 20000) {
  86. this.$message('删除失败')
  87. return
  88. }
  89. delete this.listObj[objKeyArr[i]]
  90. }).catch(err => {
  91. console.log(err)
  92. })
  93. return
  94. }
  95. }
  96. },
  97. beforeUpload(file) {
  98. const _self = this
  99. const _URL = window.URL || window.webkitURL
  100. const fileName = file.uid
  101. this.listObj[fileName] = {}
  102. return new Promise((resolve, reject) => {
  103. const img = new Image()
  104. img.src = _URL.createObjectURL(file)
  105. img.onload = function() {
  106. _self.listObj[fileName] = { hasSuccess: false, uid: file.uid, width: this.width, height: this.height }
  107. }
  108. resolve(true)
  109. })
  110. }
  111. }
  112. }
  113. </script>
  114. <style lang="scss" scoped>
  115. .editor-slide-upload {
  116. margin-bottom: 20px;
  117. /deep/ .el-upload--picture-card {
  118. width: 100%;
  119. }
  120. }
  121. </style>

至此,vue-element-admin 单独上传图片实现方式就讲完了,如有任何问题或建议欢迎提issues

links

  • 目录
  • 上一节:
  • 下一节: