1
0
mirror of https://gitee.com/bookshelfplus/bookshelfplus synced 2025-09-03 23:52:51 +08:00
Code Issues Projects Releases Wiki Activity GitHub Gitee
Files
bookshelfplus/bookshelfplus-frontend/views/dashboard/admin/FileManage_Upload.html

510 lines
20 KiB
HTML
Raw 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.

<!--
文件选择框
refer: https://developer.mozilla.org/zh-CN/docs/web/api/file/using_files_from_web_applications
-->
<style>
#dropbox {
width: 100%;
height: 200px;
display: grid;
place-items: center;
cursor: pointer;
transition: all 0.5s;
border-radius: 5px;
}
#dropbox:hover {
transform: scale(1.03);
}
.lightgrey {
background-color: lightgrey;
}
.green {
background-color: #39a705;
color: #caf6b6;
}
.grey {
background-color: grey;
color: #b5b5b5;
}
.process-bar {
background-color: #b5b5b5;
width: 100%;
height: 5px;
border-radius: 2px;
overflow: hidden;
}
.process-bar-inner {
background-color: #39a705;
width: 0;
height: 100%;
transition: all 0.3s;
}
.file-info-paragraph>span {
background-color: lightpink;
color: darkred;
padding: 1px 5px;
border-radius: 4px;
font-size: 0.8em;
}
/* 上传按钮样式 */
#beginUpload {
margin-top: 30px;
padding: 5px 12px;
transition: all 0.25s;
border: 0;
}
#beginUpload:hover {
transform: scale(1.1);
background-color: #39a705;
color: #caf6b6;
border-radius: 5px;
cursor: pointer;
}
#beginUpload[disabled]:hover {
transform: initial;
background-color: initial;
color: initial;
border-radius: initial;
cursor: initial;
}
#fileAssociator {
padding: 3px 12px;
min-width: 200px;
}
</style>
<p>
<a href="<%= pageUrl %>../">返回文件管理</a>
</p>
<div id="dropbox" class="lightgrey">
<p>
点击此处或将文件拖入此处以选择文件<br>
<!-- 文件个数: <span id="fileNum">0</span>; -->
文件大小: <span id="fileSize">0</span>
</p>
</div>
<!-- <input type="file" id="input" multiple onchange="handleFiles(this.files)"> -->
<input type="file" id="fileSelector" style="display: none;">
<p>
仅支持 Chrome 内核浏览器
</p>
<div id="file-detail-container" style="display: none;">
<p style="text-align: left;" class="file-info-paragraph">
文件名: <span id="file-name"></span><br>
文件扩展名:<span id="file-ext"></span><br>
文件名(不含扩展名):<span id="file-name-no-ext"></span><br>
文件大小: <span id="file-size"></span><br>
修改日期: <span id="file-lastModified"></span><br>
文件SHA1: <span id="file-sha1"></span><br>
</p>
<p>计算文件哈希进度</p>
<div id="processBar1" class="process-bar">
<div id="processBar1Inner" class="process-bar-inner"></div>
</div>
<p>文件上传进度</p>
<div id="processBar2" class="process-bar">
<div id="processBar2Inner" class="process-bar-inner"></div>
</div>
<p>关联文件<br><span style="font-size: 12px;">仅可关联系统中未设置SHA1或SHA1值相同的文件记录</span></p>
<select id="fileAssociator">
<!-- <option value="0">新建文件</option> -->
</select>
</div>
<button id="beginUpload" disabled="true">开始上传</button>
<p>
<input type="checkbox" id="checkbox-auto-upload"><!-- checked -->
<label for="checkbox-auto-upload" style="font-size: small;">连续上传(上传完成后留在本页)</label>
</p>
<script src="/assets/lib/crypto-js/sha1.js"></script>
<script>
var file = null;
var fileInfo = {
fileName: "",
fileSize: 0,
fileType: "",
lastModified: "",
fileSha1: "",
fileExt: "",
fileNameWithoutExt: "",
};
const inputElement = document.getElementById("fileSelector");
inputElement.addEventListener("change", fileSelectorHandleFiles, false);
const beginUpload = document.getElementById("beginUpload");
beginUpload.addEventListener("click", upload);
//##############################################
// 选择文件
//##############################################
function fileSelectorHandleFiles() {
const fileList = this.files; /* now you can work with the file list */
// console.log(fileList);
handleFiles(fileList);
}
async function handleFiles(fileList) {
updateUI({ isFileEmpty: true });
if (fileList.length === 0) {
// 还未选择文件
return;
}
if (fileList.length > 1) {
swal("一次只能选择1个文件");
return;
}
// 获取用户选择的文件
file = fileList[0];
console.log(file);
// // 判断用户选择的文件是否为文件夹
// if (await isFolder(file)) {
// swal("不支持文件夹,请选择文件!");
// return;
// }
// 输出所选文件的总大小
let nBytes = 0;
for (let nFileId = 0; nFileId < fileList.length; nFileId++) {
nBytes += fileList[nFileId].size;
}
let sOutput = nBytes + " bytes";
// optional code for multiples approximation
const aMultiples = ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
for (nMultiple = 0, nApprox = nBytes / 1024; nApprox > 1; nApprox /= 1024, nMultiple++) {
sOutput = nApprox.toFixed(2) + " " + aMultiples[nMultiple] + " (" + nBytes + " bytes)";
}
// document.getElementById("fileNum").innerHTML = fileList.length;
document.getElementById("fileSize").innerHTML = sOutput;
updateUI({ isFileEmpty: false });
// 记录文件信息
fileInfo.fileName = file.name;
fileInfo.fileSize = file.size;
fileInfo.fileType = file.type;
fileInfo.lastModified = file.lastModified;
// 获取文件扩展名:首先按照 . 拆分,然后删掉第一个元素(考虑无扩展名文件),再取出最后一个元素
let fileNameSplit = file.name.split(".");
fileNameSplit.shift();
fileInfo.fileExt = fileNameSplit.pop() || "";
// 获取文件名(不包含扩展名)
fileInfo.fileNameWithoutExt = file.name.substr(0, (file.name.length + file.name.lastIndexOf('.')) % (file.name.length));
// 更新UI
document.getElementById("file-name").innerHTML = fileInfo.fileName;
document.getElementById("file-ext").innerHTML = fileInfo.fileExt;
document.getElementById("file-name-no-ext").innerHTML = fileInfo.fileNameWithoutExt;
document.getElementById("file-size").innerHTML = sOutput;
document.getElementById("file-lastModified").innerHTML = new Date(fileInfo.lastModified).toISOString().replace(/T/, ' ').replace(/\..+/, '');
// 计算文件哈希
let sha1 = await sha1File(file, (file) => {
document.getElementById("processBar1Inner").style.width = `${file.sha1_progress}%`;
// console.log("计算文件哈希 进度", file.sha1_progress);
});
fileInfo.fileSha1 = sha1;
document.getElementById("file-sha1").innerHTML = fileInfo.fileSha1;
// 获取关联文件下拉框列表并渲染
getFileAssociatorList(sha1);
// 完成
console.log(fileInfo);
}
// dropbox 点击时触发 file 的 click 事件
const fileSelect = document.getElementById("dropbox");
fileSelect.addEventListener("click", function (e) {
if (inputElement) {
inputElement.click();
}
}, false);
//##############################################
// 使用拖放来选择文件
//##############################################
let dropbox = document.getElementById("dropbox");
dropbox.addEventListener("dragenter", dragenter, false);
dropbox.addEventListener("dragleave", dragleave, false);
dropbox.addEventListener("dragover", dragover, false);
dropbox.addEventListener("drop", drop, false);
var lastenter = null; // refer: https://www.jianshu.com/p/f96b754032a1
function dragenter(e) {
e.stopPropagation();
e.preventDefault();
lastenter = e.target; // 记录最后进入的元素
}
function dragleave(e) {
if (lastenter == e.target)
dropbox.classList.remove('grey');
}
function dragover(e) {
e.stopPropagation();
e.preventDefault();
dropbox.classList.add('grey');
}
function drop(e) {
e.stopPropagation();
e.preventDefault();
dropbox.classList.remove('grey');
var dt = e.dataTransfer;
var files = dt.files;
console.log("dt", dt.items);
console.log("dt", dt.items[0]);
console.log("dt", dt.items[0].webkitGetAsEntry());
updateUI({ isFileEmpty: true });
if (dt.items.length > 1) {
swal("一次只能选择1个文件");
return;
}
if (dt.items[0].webkitGetAsEntry().isDirectory) {
swal("不支持文件夹,请选择文件!");
return;
}
handleFiles(files);
}
// //##############################################
// // 判断拖入的文件是否是文件夹 (非 Chrome 内核浏览器逻辑)
// // 读取文件的第一个字符,如果能够读取成功,那么就是文件,否则就是文件夹
// // refer: https://segmentfault.com/a/1190000013298317
// //##############################################
// async function isFolder(file) {
// return await new Promise((resolve, reject) => {
// try {
// var fileReader = new FileReader();
// fileReader.addEventListener('load', function (e) {
// console.log(e, 'load');
// resolve(false);
// }, false);
// fileReader.addEventListener('error', function (e) {
// console.log(e, 'error不可以上传文件夹');
// resolve(true);
// }, false);
// fileReader.readAsDataURL(file.slice(0, 3));
// } catch (e) {
// console.log(e, 'catch error不可以上传文件夹');
// resolve(true);
// }
// });
// }
//##############################################
// 更新界面显示
//##############################################
function updateUI({ isFileEmpty }) {
if (isFileEmpty) {
// 未选择文件
this.outerHTML = this.outerHTML; // 清除选择的文件
file = null;
fileInfo = {};
document.getElementById("fileSize").innerHTML = "0";
document.getElementById("processBar1Inner").style.width = 0;
document.getElementById("processBar2Inner").style.width = 0;
document.getElementById("file-name").innerHTML = "";
document.getElementById("file-ext").innerHTML = "";
document.getElementById("file-name-no-ext").innerHTML = "";
document.getElementById("file-size").innerHTML = "";
document.getElementById("file-sha1").innerHTML = "";
dropbox.classList.remove("green");
beginUpload.setAttribute("disabled", "true");
document.getElementById("file-detail-container").style.display = "none";
} else {
// 已选择文件
dropbox.classList.add("green");
beginUpload.removeAttribute("disabled");
document.getElementById("file-detail-container").style.display = "";
}
}
//##############################################
// 获取预授权URL
//##############################################
function upload() {
if (!file) {
swal("您还未选择文件!");
return;
}
var postParams = {
token: localStorageUtils.getToken(),
expireMinute: 30,
fileName: fileInfo.fileNameWithoutExt,
fileSize: fileInfo.fileSize,
// fileType: fileInfo.fileType,
lastModified: fileInfo.lastModified,
fileSha1: fileInfo.fileSha1,
fileExt: fileInfo.fileExt,
fileId: $("#fileAssociator").val() // 关联的文件ID创建新文件则为0
};
console.log(postParams);
// 获取预授权URL
postRequest("/file/object/cos/put", postParams)
.then(function (response) {
var axiosData = response.data;
var status = axiosData.status;
var data = axiosData.data;
if (status === "success") {
console.log("data", data);
// 取得预授权URL使用该URL进行文件上传
uploadFile(file, data.url, data.fileId, data.fileObjectId);
} else {
if (data.errCode == "60001") {
// 文件已存在于对象存储中
console.log(`出错啦!${data.errMsg} (错误码: ${data.errCode})`);
// 再次发送请求,查询这个已存在文件的 fileId
postRequest("/file/getFileByHash", { token: localStorageUtils.getToken(), fileSha1: fileInfo.fileSha1 })
.then(function (responseData) {
var axiosData = responseData.data;
var status = axiosData.status;
var data = axiosData.data;
if (status === "success") {
console.log(data);
if (data) {
// 查询到之后,询问用户是否跳转到文件详情页
var isRedirect = confirm(`文件已存在,是否前往查看详情?\n文件ID: ${data.id}`);
if (isRedirect)
location.href = `<%= pageUrl %>../detail?fileId=${data.id}`;
} else {
// 对象存储中存在该文件,但是系统数据库中找不到
swal("出错啦!该文件已存在于腾讯云对象存储中,但是系统中不存在")
}
} else {
swal(`出错啦!${data.errMsg} (错误码: ${data.errCode})`);
}
}).catch(function (error) {
console.log(error);
swal("无法连接到服务器,请检查网络连接!");
});
} else {
swal(`出错啦!${data.errMsg} (错误码: ${data.errCode})`);
}
}
}).catch(function (error) {
console.log(error);
swal("无法连接到服务器,请检查网络连接!");
});
}
//##############################################
//传入预授权 URL ,将文件上传到这个地址
//##############################################
function uploadFile(file, preSignedUrl, fileId, fileObjectId) {
// refer: https://cloud.tencent.com/document/product/436/35651
// 获取到 Url 后,前端可以这样 ajax 上传
var xhr = new XMLHttpRequest();
xhr.open('PUT', preSignedUrl, true);
xhr.upload.onprogress = function (e) {
// e.lengthComputable是一个布尔值,表示当前上传的资源是否有可计算的长度
if (e.lengthComputable) {
// 计算出上传的进度
// e.loaded 已传输的字节
// e.total 需传输的总字节
var procentComplete = Math.ceil((e.loaded / e.total) * 100);
document.getElementById("processBar2Inner").style.width = `${procentComplete}%`;
console.log("上传进度", procentComplete);
}
};
xhr.onload = function (e) {
console.log('上传成功', xhr.status, xhr.statusText);
// 等待进度条走到 100% 否则小文件进度条还没有走完就提示上传完成会让人感觉有点奇怪
setTimeout(function () {
// 上传成功触发一次 “刷新文件对象上传状态”,避免因为云函数回调不成功导致文件对象上传状态没有及时更新
refreshFileObjectStatus(fileObjectId, function () {
swal("上传成功!").then(function () {
if ($("#checkbox-auto-upload").is(":checked")) {
window.location.reload();
} else {
// location.href = "<%= pageUrl %>../";
location.href = "<%= pageUrl %>../detail?id=" + fileId;
}
});
});
}, 300);
};
xhr.onerror = function (e) {
console.log('上传出错', xhr.status, xhr.statusText);
};
xhr.send(file); // file 是要上传的文件对象
}
</script>
<script>
// 关联文件下拉框列表
function getFileAssociatorList(fileSha1) {
var fileAssociator = document.getElementById("fileAssociator");
// 下拉框列表
postRequest("/file/list/MatchfileHashWithNullValue", { token: localStorageUtils.getToken(), fileSha1: fileSha1 })
.then(function (responseData) {
var axiosData = responseData.data;
var status = axiosData.status;
var data = axiosData.data;
if (status === "success") {
// console.log(data);
// 数据进行转换
var optionHTML = "";
for (var i = 0; i < data.length; i++) {
const element = data[i];
optionHTML += `<option value="${element.id}">[${element.id}] ${element.fileName}.${element.fileExt}</option>`;
}
// 渲染到下拉框内
// fileAssociator.innerHTML += optionHTML;
fileAssociator.innerHTML = `<option value="0">新建文件</option>` + optionHTML;
} else {
swal(`出错啦!${data.errMsg} (错误码: ${data.errCode})`);
}
}).catch(function (error) {
console.log(error);
swal("无法连接到服务器,请检查网络连接!");
});
}
</script>
<script>
// 刷新文件对象上传状态
async function refreshFileObjectStatus(fileObjectId, callback) {
postRequest("/file/object/refreshFileObjectStatus", { token: localStorageUtils.getToken(), fileObjectId: fileObjectId })
.then(function (responseData) {
var axiosData = responseData.data;
var status = axiosData.status;
var data = axiosData.data;
if (status === "success") {
// console.log(data);
if (callback)
callback();
} else {
swal(`出错啦!${data.errMsg} (错误码: ${data.errCode})`);
}
}).catch(function (error) {
console.log(error);
swal("无法连接到服务器,请检查网络连接!");
});
}
</script>