wzp
2021-07-19 e65183d31755a0e5fae4bf428435d2e0fd6afdc5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
/*
    上传图片
*/
 
import { objForEach, arrForEach, percentFormat } from '../../util/util.js'
import Progress from './progress.js'
import { UA } from '../../util/util.js'
 
// 构造函数
function UploadImg(editor) {
    this.editor = editor
}
 
// 原型
UploadImg.prototype = {
    constructor: UploadImg,
 
    // 根据 debug 弹出不同的信息
    _alert: function (alertInfo, debugInfo) {
        const editor = this.editor
        const debug = editor.config.debug
        const customAlert = editor.config.customAlert
 
        if (debug) {
            throw new Error('wangEditor: ' + (debugInfo || alertInfo))
        } else {
            if (customAlert && typeof customAlert === 'function') {
                customAlert(alertInfo)
            } else {
                alert(alertInfo)
            }
        }
    },
 
    // 根据链接插入图片
    insertLinkImg: function (link) {
        if (!link) {
            return
        }
        const editor = this.editor
        const config = editor.config
 
        // 校验格式
        const linkImgCheck = config.linkImgCheck
        let checkResult
        if (linkImgCheck && typeof linkImgCheck === 'function') {
            checkResult = linkImgCheck(link)
            if (typeof checkResult === 'string') {
                // 校验失败,提示信息
                alert(checkResult)
                return
            }
        }
 
        editor.cmd.do('insertHTML', `<img src="${link}" style="max-width:100%;"/>`)
 
        // 验证图片 url 是否有效,无效的话给出提示
        let img = document.createElement('img')
        img.onload = () => {
            const callback = config.linkImgCallback
            if (callback && typeof callback === 'function') {
                callback(link)
            }
 
            img = null
        }
        img.onerror = () => {
            img = null
            // 无法成功下载图片
            this._alert('插入图片错误', `wangEditor: 插入图片出错,图片链接是 "${link}",下载该链接失败`)
            return
        }
        img.onabort = () => {
            img = null
        }
        img.src = link
    },
 
    // 上传图片
    uploadImg: function (files) {
        if (!files || !files.length) {
            return
        }
 
        // ------------------------------ 获取配置信息 ------------------------------
        const editor = this.editor
        const config = editor.config
        let uploadImgServer = config.uploadImgServer
        const uploadImgShowBase64 = config.uploadImgShowBase64
 
        const maxSize = config.uploadImgMaxSize
        const maxSizeM = maxSize / 1024 / 1024
        const maxLength = config.uploadImgMaxLength || 10000
        const uploadFileName = config.uploadFileName || ''
        const uploadImgParams = config.uploadImgParams || {}
        const uploadImgParamsWithUrl = config.uploadImgParamsWithUrl
        const uploadImgHeaders = config.uploadImgHeaders || {}
        const hooks = config.uploadImgHooks || {}
        const timeout = config.uploadImgTimeout || 3000
        let withCredentials = config.withCredentials
        if (withCredentials == null) {
            withCredentials = false
        }
        const customUploadImg = config.customUploadImg
 
        if (!customUploadImg) {
            // 没有 customUploadImg 的情况下,需要如下两个配置才能继续进行图片上传
            if (!uploadImgServer && !uploadImgShowBase64) {
                return
            }
        }
 
        // ------------------------------ 验证文件信息 ------------------------------
        const resultFiles = []
        let errInfo = []
        arrForEach(files, file => {
            var name = file.name
            var size = file.size
 
            // chrome 低版本 name === undefined
            if (!name || !size) {
                return
            }
 
            if (/\.(jpg|jpeg|png|bmp|gif|webp)$/i.test(name) === false) {
                // 后缀名不合法,不是图片
                errInfo.push(`【${name}】不是图片`)
                return
            }
            if (maxSize < size) {
                // 上传图片过大
                errInfo.push(`【${name}】大于 ${maxSizeM}M`)
                return
            }
 
            // 验证通过的加入结果列表
            resultFiles.push(file)
        })
        // 抛出验证信息
        if (errInfo.length) {
            this._alert('图片验证未通过: \n' + errInfo.join('\n'))
            return
        }
        if (resultFiles.length > maxLength) {
            this._alert('一次最多上传' + maxLength + '张图片')
            return
        }
 
        // ------------------------------ 自定义上传 ------------------------------
        if (customUploadImg && typeof customUploadImg === 'function') {
            customUploadImg(resultFiles, this.insertLinkImg.bind(this))
 
            // 阻止以下代码执行
            return
        }
 
        // 添加图片数据
        const formdata = new FormData()
        arrForEach(resultFiles, file => {
            const name = uploadFileName || file.name
            formdata.append(name, file)
        })
 
        // ------------------------------ 上传图片 ------------------------------
        if (uploadImgServer && typeof uploadImgServer === 'string') {
            // 添加参数
            const uploadImgServerArr = uploadImgServer.split('#')
            uploadImgServer = uploadImgServerArr[0]
            const uploadImgServerHash = uploadImgServerArr[1] || ''
            objForEach(uploadImgParams, (key, val) => {
                // 因使用者反应,自定义参数不能默认 encode ,由 v3.1.1 版本开始注释掉
                // val = encodeURIComponent(val)
 
                // 第一,将参数拼接到 url 中
                if (uploadImgParamsWithUrl) {
                    if (uploadImgServer.indexOf('?') > 0) {
                        uploadImgServer += '&'
                    } else {
                        uploadImgServer += '?'
                    }
                    uploadImgServer = uploadImgServer + key + '=' + val
                }
 
                // 第二,将参数添加到 formdata 中
                formdata.append(key, val)
            })
            if (uploadImgServerHash) {
                uploadImgServer += '#' + uploadImgServerHash
            }
 
            // 定义 xhr
            const xhr = new XMLHttpRequest()
            xhr.open('POST', uploadImgServer)
 
            // 设置超时
            xhr.timeout = timeout
            xhr.ontimeout = () => {
                // hook - timeout
                if (hooks.timeout && typeof hooks.timeout === 'function') {
                    hooks.timeout(xhr, editor)
                }
 
                this._alert('上传图片超时')
            }
 
            // 监控 progress
            if (xhr.upload) {
                xhr.upload.onprogress = e => {
                    let percent
                    // 进度条
                    const progressBar = new Progress(editor)
                    if (e.lengthComputable) {
                        percent = e.loaded / e.total
                        progressBar.show(percent)
                    }
                }
            }
 
            // 返回数据
            xhr.onreadystatechange = () => {
                let result
                if (xhr.readyState === 4) {
                    if (xhr.status < 200 || xhr.status >= 300) {
                        // hook - error
                        if (hooks.error && typeof hooks.error === 'function') {
                            hooks.error(xhr, editor)
                        }
 
                        // xhr 返回状态错误
                        this._alert('上传图片发生错误', `上传图片发生错误,服务器返回状态是 ${xhr.status}`)
                        return
                    }
 
                    result = xhr.responseText
                    if (typeof result !== 'object') {
                        try {
                            result = JSON.parse(result)
                        } catch (ex) {
                            // hook - fail
                            if (hooks.fail && typeof hooks.fail === 'function') {
                                hooks.fail(xhr, editor, result)
                            }
 
                            this._alert('上传图片失败', '上传图片返回结果错误,返回结果是: ' + result)
                            return
                        }
                    }
                    if (!hooks.customInsert && result.errno != '0') {
                        // hook - fail
                        if (hooks.fail && typeof hooks.fail === 'function') {
                            hooks.fail(xhr, editor, result)
                        }
 
                        // 数据错误
                        this._alert('上传图片失败', '上传图片返回结果错误,返回结果 errno=' + result.errno)
                    } else {
                        if (hooks.customInsert && typeof hooks.customInsert === 'function') {
                            // 使用者自定义插入方法
                            hooks.customInsert(this.insertLinkImg.bind(this), result, editor)
                        } else {
                            // 将图片插入编辑器
                            const data = result.data || []
                            data.forEach(link => {
                                this.insertLinkImg(link)
                            })
                        }
 
                        // hook - success
                        if (hooks.success && typeof hooks.success === 'function') {
                            hooks.success(xhr, editor, result)
                        }
                    }
                }
            }
 
            // hook - before
            if (hooks.before && typeof hooks.before === 'function') {
                const beforeResult = hooks.before(xhr, editor, resultFiles)
                if (beforeResult && typeof beforeResult === 'object') {
                    if (beforeResult.prevent) {
                        // 如果返回的结果是 {prevent: true, msg: 'xxxx'} 则表示用户放弃上传
                        this._alert(beforeResult.msg)
                        return
                    }
                }
            }
 
            // 自定义 headers
            objForEach(uploadImgHeaders, (key, val) => {
                xhr.setRequestHeader(key, val)
            })
 
            // 跨域传 cookie
            xhr.withCredentials = withCredentials
 
            // 发送请求
            xhr.send(formdata)
 
            // 注意,要 return 。不去操作接下来的 base64 显示方式
            return
        }
 
        // ------------------------------ 显示 base64 格式 ------------------------------
        if (uploadImgShowBase64) {
            arrForEach(files, file => {
                const _this = this
                const reader = new FileReader()
                reader.readAsDataURL(file)
                reader.onload = function () {
                    _this.insertLinkImg(this.result)
                }
            })
        }
    }
}
 
export default UploadImg