* `chunk-enabled` prop added to enable chunk upload * `chunk` prop added to modify chunk upload parameters * `ChunkUploadHandler` class created to handler chunk upload process * Example added to the docs * Chunk documentation added to the docsmaster
parent
7d88edc716
commit
872c984eb2
@ -0,0 +1,38 @@
|
|||||||
|
// http://eslint.org/docs/user-guide/configuring
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
parser: 'babel-eslint',
|
||||||
|
parserOptions: {
|
||||||
|
sourceType: 'module'
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
jest: true
|
||||||
|
},
|
||||||
|
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
|
||||||
|
extends: "standard",
|
||||||
|
|
||||||
|
// required to lint *.vue files
|
||||||
|
plugins: [
|
||||||
|
'html'
|
||||||
|
],
|
||||||
|
// add your custom rules here
|
||||||
|
'rules': {
|
||||||
|
// allow paren-less arrow functions
|
||||||
|
'arrow-parens': 0,
|
||||||
|
// allow async-await
|
||||||
|
'generator-star-spacing': 0,
|
||||||
|
// allow debugger during development
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
||||||
|
|
||||||
|
'no-useless-escape': 0,
|
||||||
|
'comma-dangle': 0,
|
||||||
|
'space-before-function-paren': 0,
|
||||||
|
'no-multiple-empty-lines': 0,
|
||||||
|
'no-multi-spaces': 0,
|
||||||
|
'padded-blocks': 0,
|
||||||
|
'prefer-promise-reject-errors': 0,
|
||||||
|
'operator-linebreak': 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,245 @@
|
|||||||
|
<template>
|
||||||
|
<div class="example-simple">
|
||||||
|
<h1 id="example-title" class="example-title">Chunk Upload Example</h1>
|
||||||
|
|
||||||
|
<p>When using chunk uploads, the file will be uploaded in different parts (or chunks). All the files with a size higher than the set in the input will be uploaded using this method.</p>
|
||||||
|
<p>You will be able to see the different parts being uploaded individually. Some parts might fail, and the package is prepared to <em>retry</em> up to a certain amount of times.</p>
|
||||||
|
<p>You can also pause / resume the upload process.</p>
|
||||||
|
|
||||||
|
<div class="upload">
|
||||||
|
<div class="form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input v-model="chunkEnabled" type="checkbox"> Use chunk upload
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputMinSize" class="col-sm-2 control-label">Min Size</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="inputMinSize" v-model="chunkMinSize" type="number" class="form-control">
|
||||||
|
<span class="input-group-addon">MB</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputMaxActive" class="col-sm-2 control-label">Max Active Chunks</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input id="inputMaxActive" v-model="chunkMaxActive" type="number" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputMaxRetries" class="col-sm-2 control-label">Max Chunk Retries</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input id="inputMaxRetries" v-model="chunkMaxRetries" type="number" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="table table-striped table-condensed">
|
||||||
|
<thead class="thead-dark">
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th class="text-right">Size</th>
|
||||||
|
<th class="text-right">Progress</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Pause</th>
|
||||||
|
<th colspan="3" class="text-center">Chunks</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="5"></th>
|
||||||
|
<th class="text-right">Total</th>
|
||||||
|
<th class="text-right">Active</th>
|
||||||
|
<th class="text-right">Completed</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<template v-for="file in files">
|
||||||
|
<tr :key="file.id + '-info'">
|
||||||
|
<td>{{ file.name }}</td>
|
||||||
|
<td class="text-right">{{ file.size | formatSize }}</td>
|
||||||
|
<td class="text-right">{{ file.progress }}%</td>
|
||||||
|
|
||||||
|
<td v-if="file.error">{{ file.error }}</td>
|
||||||
|
<td v-else-if="file.success">Success</td>
|
||||||
|
<td v-else-if="file.active">Active</td>
|
||||||
|
<td v-else> - </td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<template v-if="file.chunk">
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-danger"
|
||||||
|
v-if="file.active"
|
||||||
|
@click="file.chunk.pause()"
|
||||||
|
>
|
||||||
|
<i class="fa fa-pause"/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-primary"
|
||||||
|
v-if="!file.active && file.chunk.hasChunksToUpload"
|
||||||
|
@click="file.chunk.resume()"
|
||||||
|
>
|
||||||
|
<i class="fa fa-play"/>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<template v-if="file.chunk">
|
||||||
|
<td class="text-right">{{ file.chunk.chunks.length }}</td>
|
||||||
|
<td class="text-right">{{ file.chunk.chunksUploading.length }}</td>
|
||||||
|
<td class="text-right">{{ file.chunk.chunksUploaded.length }}</td>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<td class="text-right"> - </td>
|
||||||
|
<td class="text-right"> - </td>
|
||||||
|
<td class="text-right"> - </td>
|
||||||
|
</template>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr :key="file.id + '-loading'">
|
||||||
|
<td colspan="8">
|
||||||
|
<div class="chunk-loading" v-if="file.chunk">
|
||||||
|
<span
|
||||||
|
v-for="(chunk, index) in file.chunk.chunks"
|
||||||
|
:key="index"
|
||||||
|
class="chunk-loading-part"
|
||||||
|
:class="{'chunk-loading-part__uploaded': chunk.uploaded}"
|
||||||
|
>
|
||||||
|
<template v-if="chunk.retries != file.chunk.maxRetries">
|
||||||
|
{{ file.chunk.maxRetries - chunk.retries }} error(s)
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="example-btn">
|
||||||
|
<file-upload
|
||||||
|
class="btn btn-primary"
|
||||||
|
post-action="/upload/post"
|
||||||
|
|
||||||
|
:chunk-enabled="chunkEnabled"
|
||||||
|
:chunk="{
|
||||||
|
action: '/upload/chunk',
|
||||||
|
minSize: chunkMinSize * 1048576,
|
||||||
|
maxActive: chunkMaxActive,
|
||||||
|
maxRetries: chunkMaxRetries
|
||||||
|
}"
|
||||||
|
|
||||||
|
extensions="gif,jpg,jpeg,png,webp"
|
||||||
|
accept="image/png,image/gif,image/jpeg,image/webp"
|
||||||
|
:multiple="true"
|
||||||
|
:size="1024 * 1024 * 10"
|
||||||
|
v-model="files"
|
||||||
|
@input-filter="inputFilter"
|
||||||
|
@input-file="inputFile"
|
||||||
|
ref="upload">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
Select files
|
||||||
|
</file-upload>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pt-5">
|
||||||
|
Source code: <a href="https://github.com/lian-yue/vue-upload-component/blob/master/docs/views/examples/Chunk.vue">/docs/views/examples/Chunk.vue</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style>
|
||||||
|
.example-simple label.btn {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import FileUpload from 'vue-upload-component'
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
FileUpload,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
files: [],
|
||||||
|
|
||||||
|
chunkEnabled: true,
|
||||||
|
|
||||||
|
// 1MB by default
|
||||||
|
chunkMinSize: 1,
|
||||||
|
chunkMaxActive: 3,
|
||||||
|
chunkMaxRetries: 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
inputFilter(newFile, oldFile, prevent) {
|
||||||
|
if (newFile && !oldFile) {
|
||||||
|
// Before adding a file
|
||||||
|
// 添加文件前
|
||||||
|
|
||||||
|
// Filter system files or hide files
|
||||||
|
// 过滤系统文件 和隐藏文件
|
||||||
|
if (/(\/|^)(Thumbs\.db|desktop\.ini|\..+)$/.test(newFile.name)) {
|
||||||
|
return prevent()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter php html js file
|
||||||
|
// 过滤 php html js 文件
|
||||||
|
if (/\.(php5?|html?|jsx?)$/i.test(newFile.name)) {
|
||||||
|
return prevent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
inputFile(newFile, oldFile) {
|
||||||
|
if (newFile && !oldFile) {
|
||||||
|
// add
|
||||||
|
console.log('add', newFile)
|
||||||
|
this.$refs.upload.active = true
|
||||||
|
}
|
||||||
|
if (newFile && oldFile) {
|
||||||
|
// update
|
||||||
|
console.log('update', newFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!newFile && oldFile) {
|
||||||
|
// remove
|
||||||
|
console.log('remove', oldFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.chunk-loading {
|
||||||
|
margin: -12px;
|
||||||
|
display: flex;
|
||||||
|
width: calc(100% + 24px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chunk-loading .chunk-loading-part {
|
||||||
|
height: 25px;
|
||||||
|
line-height: 25px;
|
||||||
|
flex: 1;
|
||||||
|
background: #ccc;
|
||||||
|
font-size: 14px;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chunk-loading .chunk-loading-part.chunk-loading-part__uploaded {
|
||||||
|
background: #28A745;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,329 @@
|
|||||||
|
import { default as request, createRequest, sendRequest } from '../utils/request'
|
||||||
|
|
||||||
|
export default class ChunkUploadHandler {
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param {File} file
|
||||||
|
* @param {Object} options
|
||||||
|
*/
|
||||||
|
constructor (file, options) {
|
||||||
|
this.file = file
|
||||||
|
this.options = options
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the max retries from options
|
||||||
|
*/
|
||||||
|
get maxRetries () {
|
||||||
|
return parseInt(this.options.maxRetries)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the max number of active chunks being uploaded at once from options
|
||||||
|
*/
|
||||||
|
get maxActiveChunks () {
|
||||||
|
return parseInt(this.options.maxActive)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the file type
|
||||||
|
*/
|
||||||
|
get fileType () {
|
||||||
|
return this.file.type
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the file size
|
||||||
|
*/
|
||||||
|
get fileSize () {
|
||||||
|
return this.file.size
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets action (url) to upload the file
|
||||||
|
*/
|
||||||
|
get action () {
|
||||||
|
return this.options.action || null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the body to be merged when sending the request in start phase
|
||||||
|
*/
|
||||||
|
get startBody () {
|
||||||
|
return this.options.startBody || {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the body to be merged when sending the request in upload phase
|
||||||
|
*/
|
||||||
|
get uploadBody () {
|
||||||
|
return this.options.uploadBody || {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the body to be merged when sending the request in finish phase
|
||||||
|
*/
|
||||||
|
get finishBody () {
|
||||||
|
return this.options.finishBody || {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the headers of the requests from options
|
||||||
|
*/
|
||||||
|
get headers () {
|
||||||
|
return this.options.headers || {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether it's ready to upload files or not
|
||||||
|
*/
|
||||||
|
get readyToUpload () {
|
||||||
|
return !!this.chunks
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the progress of the chunk upload
|
||||||
|
* - Gets all the completed chunks
|
||||||
|
* - Gets the progress of all the chunks that are being uploaded
|
||||||
|
*/
|
||||||
|
get progress () {
|
||||||
|
const completedProgress = (this.chunksUploaded.length / this.chunks.length) * 100
|
||||||
|
const uploadingProgress = this.chunksUploading.reduce((progress, chunk) => {
|
||||||
|
return progress + ((chunk.progress | 0) / this.chunks.length)
|
||||||
|
}, 0)
|
||||||
|
|
||||||
|
return Math.min(completedProgress + uploadingProgress, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all the chunks that are pending to be uploaded
|
||||||
|
*/
|
||||||
|
get chunksToUpload () {
|
||||||
|
return this.chunks.filter(chunk => {
|
||||||
|
return !chunk.active && !chunk.uploaded
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether there are chunks to upload or not
|
||||||
|
*/
|
||||||
|
get hasChunksToUpload () {
|
||||||
|
return this.chunksToUpload.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all the chunks that are uploading
|
||||||
|
*/
|
||||||
|
get chunksUploading () {
|
||||||
|
return this.chunks.filter(chunk => {
|
||||||
|
return !!chunk.xhr && !!chunk.active
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all the chunks that have finished uploading
|
||||||
|
*/
|
||||||
|
get chunksUploaded () {
|
||||||
|
return this.chunks.filter(chunk => {
|
||||||
|
return !!chunk.uploaded
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates all the chunks in the initial state
|
||||||
|
*/
|
||||||
|
createChunks () {
|
||||||
|
this.chunks = []
|
||||||
|
|
||||||
|
let start = 0
|
||||||
|
let end = this.chunkSize
|
||||||
|
while (start < this.fileSize) {
|
||||||
|
this.chunks.push({
|
||||||
|
blob: this.file.file.slice(start, end),
|
||||||
|
startOffset: start,
|
||||||
|
active: false,
|
||||||
|
retries: this.maxRetries
|
||||||
|
})
|
||||||
|
start = end
|
||||||
|
end = start + this.chunkSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the progress of the file with the handler's progress
|
||||||
|
*/
|
||||||
|
updateFileProgress () {
|
||||||
|
this.file.progress = this.progress
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paues the upload process
|
||||||
|
* - Stops all active requests
|
||||||
|
* - Sets the file not active
|
||||||
|
*/
|
||||||
|
pause () {
|
||||||
|
this.file.active = false
|
||||||
|
this.chunksUploading.forEach(chunk => {
|
||||||
|
chunk.xhr.abort()
|
||||||
|
chunk.active = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resumes the file upload
|
||||||
|
* - Sets the file active
|
||||||
|
* - Starts the following chunks
|
||||||
|
*/
|
||||||
|
resume () {
|
||||||
|
this.file.active = true
|
||||||
|
this.startChunking()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the file upload
|
||||||
|
*
|
||||||
|
* @returns Promise
|
||||||
|
* - resolve The file was uploaded
|
||||||
|
* - reject The file upload failed
|
||||||
|
*/
|
||||||
|
upload () {
|
||||||
|
this.promise = new Promise((resolve, reject) => {
|
||||||
|
this.resolve = resolve
|
||||||
|
this.reject = reject
|
||||||
|
})
|
||||||
|
this.start()
|
||||||
|
|
||||||
|
return this.promise
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start phase
|
||||||
|
* Sends a request to the backend to initialise the chunks
|
||||||
|
*/
|
||||||
|
start () {
|
||||||
|
request({
|
||||||
|
method: 'POST',
|
||||||
|
headers: this.headers,
|
||||||
|
url: this.action,
|
||||||
|
body: Object.assign(this.startBody, {
|
||||||
|
phase: 'start',
|
||||||
|
mime_type: this.fileType,
|
||||||
|
size: this.fileSize
|
||||||
|
})
|
||||||
|
}).then(res => {
|
||||||
|
if (res.status !== 'success') {
|
||||||
|
return this.reject(res.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sessionId = res.data.session_id
|
||||||
|
this.chunkSize = res.data.end_offset
|
||||||
|
|
||||||
|
this.createChunks()
|
||||||
|
this.startChunking()
|
||||||
|
}).catch(error => this.reject(error))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts to upload chunks
|
||||||
|
*/
|
||||||
|
startChunking () {
|
||||||
|
for (let i = 0; i < this.maxActiveChunks; i++) {
|
||||||
|
this.uploadNextChunk()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads the next chunk
|
||||||
|
* - Won't do anything if the process is paused
|
||||||
|
* - Will start finish phase if there are no more chunks to upload
|
||||||
|
*/
|
||||||
|
uploadNextChunk () {
|
||||||
|
if (this.file.active) {
|
||||||
|
if (this.hasChunksToUpload) {
|
||||||
|
return this.uploadChunk(this.chunksToUpload[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.chunksUploading.length === 0) {
|
||||||
|
return this.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads a chunk
|
||||||
|
* - Sends the chunk to the backend
|
||||||
|
* - Sets the chunk as uploaded if everything went well
|
||||||
|
* - Decreases the number of retries if anything went wrong
|
||||||
|
* - Fails if there are no more retries
|
||||||
|
*
|
||||||
|
* @param {Object} chunk
|
||||||
|
*/
|
||||||
|
uploadChunk (chunk) {
|
||||||
|
chunk.progress = 0
|
||||||
|
chunk.active = true
|
||||||
|
this.updateFileProgress()
|
||||||
|
chunk.xhr = createRequest({
|
||||||
|
method: 'POST',
|
||||||
|
headers: this.headers,
|
||||||
|
url: this.action
|
||||||
|
})
|
||||||
|
|
||||||
|
chunk.xhr.upload.addEventListener('progress', function(evt) {
|
||||||
|
if (evt.lengthComputable) {
|
||||||
|
chunk.progress = Math.round(evt.loaded / evt.total * 100)
|
||||||
|
}
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
sendRequest(chunk.xhr, Object.assign(this.uploadBody, {
|
||||||
|
phase: 'upload',
|
||||||
|
session_id: this.sessionId,
|
||||||
|
start_offset: chunk.startOffset,
|
||||||
|
chunk: chunk.blob
|
||||||
|
})).then(res => {
|
||||||
|
chunk.active = false
|
||||||
|
if (res.status === 'success') {
|
||||||
|
chunk.uploaded = true
|
||||||
|
} else {
|
||||||
|
if (chunk.retries-- <= 0) {
|
||||||
|
this.pause()
|
||||||
|
return this.reject('File upload failed')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.uploadNextChunk()
|
||||||
|
}).catch(() => {
|
||||||
|
chunk.active = false
|
||||||
|
if (chunk.retries-- <= 0) {
|
||||||
|
this.pause()
|
||||||
|
return this.reject('File upload failed')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.uploadNextChunk()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finish phase
|
||||||
|
* Sends a request to the backend to finish the process
|
||||||
|
*/
|
||||||
|
finish () {
|
||||||
|
this.updateFileProgress()
|
||||||
|
|
||||||
|
request({
|
||||||
|
method: 'POST',
|
||||||
|
headers: this.headers,
|
||||||
|
url: this.action,
|
||||||
|
body: Object.assign(this.finishBody, {
|
||||||
|
phase: 'finish',
|
||||||
|
session_id: this.sessionId
|
||||||
|
})
|
||||||
|
}).then(res => {
|
||||||
|
if (res.status !== 'success') {
|
||||||
|
return this.reject(res.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resolve(res)
|
||||||
|
}).catch(error => this.reject(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* Creates a XHR request
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
*/
|
||||||
|
export const createRequest = (options) => {
|
||||||
|
const xhr = new XMLHttpRequest()
|
||||||
|
xhr.responseType = 'json'
|
||||||
|
xhr.open(options.method || 'GET', options.url)
|
||||||
|
if (options.headers) {
|
||||||
|
Object.keys(options.headers).forEach(key => {
|
||||||
|
xhr.setRequestHeader(key, options.headers[key])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return xhr
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a XHR request with certain body
|
||||||
|
*
|
||||||
|
* @param {XMLHttpRequest} xhr
|
||||||
|
* @param {Object} body
|
||||||
|
*/
|
||||||
|
export const sendRequest = (xhr, body) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
xhr.onload = () => {
|
||||||
|
if (xhr.status >= 200 && xhr.status < 300) {
|
||||||
|
resolve(xhr.response)
|
||||||
|
} else {
|
||||||
|
reject(xhr.statusText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xhr.onerror = () => reject(xhr.statusText)
|
||||||
|
xhr.send(JSON.stringify(body))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and sends XHR request
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
*
|
||||||
|
* @returns Promise
|
||||||
|
*/
|
||||||
|
export default function (options) {
|
||||||
|
const xhr = createRequest(options)
|
||||||
|
|
||||||
|
return sendRequest(xhr, options.body)
|
||||||
|
}
|
||||||
Loading…
Reference in new issue