znt/module/file/js/uploadimages.ui.js

187 lines
5.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const getChunks = (file, chunkSize) => {
const chunks = [];
let start = 0;
let end = Math.min(chunkSize, file.size);
while (start < end)
{
chunks.push(file.slice(start, end));
start = end;
end = Math.min(start + chunkSize, file.size);
}
return chunks;
};
const uploadChunk = (url, chunk, headers) => {
return fetch(url, {
method: 'POST',
body: chunk,
headers,
}).then(response => response.json()).then(json => {
if(json.result == 'fail') return Promise.reject(json);
return new Promise(resolve => setTimeout(() => resolve(json), 100)); // 增加延迟给后端处理session的时间
});
}
function uploadFileByChunk(url, file, chunkSize = 1024 * 1024, onProgress = null)
{
const chunks = getChunks(file, chunkSize);
let i = 0;
return new Promise((resolve, reject) => {
const uploadNextChunk = () => {
if(i >= chunks.length)
{
if(typeof onProgress === 'function') onProgress(1);
resolve();
return;
}
const headers = {
'X-CHUNK-INDEX': i,
'X-TOTAL-CHUNKS': chunks.length,
'X-FILENAME': encodeURIComponent(file.name),
'X-FILESIZE': file.size,
};
uploadChunk(url, chunks[i], headers)
.then(() => {
i++;
if(typeof onProgress === 'function') onProgress(i / chunks.length);
uploadNextChunk();
})
.catch(reject);
};
uploadNextChunk();
});
};
/**
* 控制并发上传数量
* @param {File[]} files
* @param {string} uploadUrl
* @param {number} chunkSize
* @param {number} maxConcurrency
*/
const uploadFilesWithConcurrency = async (files, uploadUrl, chunkSize, maxConcurrency = 2) => {
const results = [];
const inProgress = new Set();
const uploadFile = async (file, fileIndex) => {
return new Promise((resolve, reject) => {
uploadFileByChunk(uploadUrl, file, chunkSize, function(progress) {
}).then(() => {
resolve({ file, fileIndex, success: true });
}).catch(error => {
reject({ file, fileIndex, error });
});
});
};
const processQueue = async () => {
for (let i = 0; i < files.length; i++) {
if (inProgress.size >= maxConcurrency) {
await Promise.race(inProgress);
}
const file = files[i];
const uploadPromise = uploadFile(file, i).finally(() => {
inProgress.delete(uploadPromise);
});
inProgress.add(uploadPromise);
results.push(uploadPromise);
}
await Promise.all(results);
};
await processQueue();
return results;
};
window.uploadImages = function(selector, options, $uploadBtn)
{
const $fileBox = $(selector);
const fileBox = $fileBox.zui();
const files = fileBox.$.files;
if(!files.length)
{
if(!$uploadBtn.hasClass('disabled')) zui.Modal.alert(options.errorUploadEmpty);
return;
}
const progressMap = new Map();
let uploadedCount = 0;
$uploadBtn.attr('disabled', 'disabled');
$uploadBtn.find('.as-progress').text(' 0%');
const render = () =>
{
fileBox.render({disabled: true, itemProps: (file) =>
{
const progress = progressMap.get(file.file);
if(progress === undefined) return {};
if(progress === 1) return {icon: 'check text-success absolute right-0 bottom-2'};
return {title: Math.round(progress * 100) + '% | ' + file.name};
}});
};
render();
/* 串行上传文件 */
const uploadFilesSequentially = async (fileIndex = 0) => {
if (fileIndex >= files.length) {
const modalID = $uploadBtn.closest('.modal').attr('id');
zui.Modal.hide('#' + modalID);
const $form = $('body').find('form.form-batch[data-zui-batchform]');
const $modal = $form.closest('.modal')
if($modal.length > 0)
{
$.ajax(
{
url: options.locateUrl,
headers:{'X-Zui-Modal': true},
dataType: 'json',
success: function(data)
{
$modal.find('[data-zui-ajaxform]').zui('ajaxform').destroy();
$modal.find('[data-zui-batchform]').zui('batchForm').destroy();
setTimeout(function()
{
loadModal(data.load, $modal.attr('id'));
}, 500);
}
});
return;
}
loadPage(options.locateUrl);
return;
}
const file = files[fileIndex];
try {
await uploadFileByChunk(options.uploadUrl, file, options.chunkSize, function(progress)
{
progressMap.set(file, progress);
render();
$uploadBtn.find('.as-progress').text(' ' + Math.round((uploadedCount + progress) / files.length * 100) + '%' );
});
uploadedCount++;
uploadFilesSequentially(fileIndex + 1);
} catch (error) {
$uploadBtn.removeAttr('disabled');
$uploadBtn.find('.as-progress').text('');
fileBox.render({disabled: false});
if(typeof(error.message) != 'undefined') zui.Modal.alert(error.message);
}
};
uploadFilesSequentially();
};