javascript - 异步函数的问题,JS。我可以使用 Promise 吗?
问题描述
我有一些我认为现在不能正常工作的代码,因为我添加了一些东西来使用一些 JS 获取 MIME 类型(真正的 MIME 类型)。它调用 checkDicomMime(file),它异步读取要上传的文件,并确定 MIME 类型是否与我正在寻找的匹配。
我认为 MIME 类型检测正在工作,但因为读取文件需要时间,我认为其余代码在读取 MIME 类型之前执行。
以前,我只是检查文件扩展名,这是同步完成的,因此函数中的“reader.onload = function (evt) {”块中的变量是内联设置的。现在,它调用该函数,该函数正确检测到 MIME 类型,但看起来调用函数已完成,其余代码在 MIME 类型检测完成之前执行,因此它为之前列表中的每个文件发布表单MIME TYPE 检测完成。total = counts.process 现在为零,而不是要处理的文件总数,因此 counts 和 files.process 和 badfiles 要么没有改变,要么只有在所有文件都发布后才会改变。我检查了一些调试,看起来它们是在文件发送后设置的。还,其他 SO 帖子谈到仅读取必要数量的字节以检测 MIME 类型,而不是读取整个文件。不确定该怎么做。
我在这里得到了 DICOM 检查功能: 检查 Dicom
这里有一些关于使用 JS 进行 MIME 类型检测的讨论:
上传前如何使用 javascript 检查文件 MIME 类型?
相关代码为:
var counts;
// Detects when a Folder is selected, Folder, not a file.
picker.addEventListener('change', e => {
counts = {process:0,omit:0};
requestcounter = 0;
responsecounter = 0;
total = 0;
skipotherrequests = 0;
parsedepoch = new Date().toISOString().match(/(\d{4}\-\d{2}\-\d{2})T(\d{2}:\d{2}:\d{2})/);
datetimestamp = parsedepoch[1] + "-" + parsedepoch[2].replace(/:/g, "-");
//alert(datetimestamp);
picker.setAttribute('data-timestamp', datetimestamp);
// preprocess checking
var badfiles = [];
var filelist = Array.from(picker.files);
filelist.forEach(function(file, index) {
// add it to the list, otherwise skip it
checkDicomMime(file); // calls the check for MIME type.
});
filelist.sort(function(a,b) {
return a.name > b.name;
});
total = counts.process; // omitting the ones that do not pass verification.
badlist = "";
badfiles.forEach( element => badlist += '<div>' + element + '</div>' );
for (var i = 0; i < filelist.length; i++) {
var file = filelist[i];
if (file.process == 0) {
let lineitem = statusitem(file, "Skipping file: " + file.name);
listing.insertAdjacentHTML('beforeend', lineitem);
}
else {
sendFile(file); // sends form and file
}
}
});
function checkDicomMime(file) {
var reader = new FileReader();
reader.readAsArrayBuffer(file);
//Fired after sucessful file read, Please read documenation for FileReader
reader.onload = function (evt) {
if (evt.target.readyState === FileReader.DONE) {
var array = new Uint8Array(evt.target.result);
var s = "";
var start = 128, end = 132;
for (var i = start; i < end; ++i) {
s += String.fromCharCode(array[i]);
}
if (s == "DICM") {
alert("DICM a valid dicom file");
file.process = 1;
counts.process++;
}
else {
alert("DICM not found");
file.process = 0;
counts.omit++;
badfiles.push (file.name);
}
}
}
}
然后 sendFile 函数的开头是:
sendFile = function(file) {
if (skipotherrequests == 0) {
var timestamp = picker.dataset.timestamp;
var formData = new FormData();
// Set post variables
requestcounter = requestcounter + 1;
formData.set('timestamp', timestamp); // One object file
formData.set('counter', requestcounter);
formData.set('total', total);
formData.set('type', type);
formData.set('webkitpath', file.webkitRelativePath); // One object file
formData.set('file', file); // One object file
//console.log(file);
var request = new XMLHttpRequest();
request.responseType = 'json';
// HTTP onload handler
request.onload = function() {
if (request.readyState === request.DONE) {
解决方案
现在,它调用该函数,该函数正确检测到 MIME 类型,但看起来调用函数已完成,其余代码在 MIME 类型检测完成之前执行,因此它为之前列表中的每个文件发布表单MIME TYPE 检测完成。
您可以更改checkDicomMime
为 promise 并等待所有文件都被检查。
然后你可以继续循环处理它们并像你一样发送有效的。
当然,这需要一些代码重构。
例子
const picker = document.querySelector("#file");
const listing = document.querySelector("#listing");
const button = document.querySelector("#button");
picker.addEventListener('change', async event => {
const counts = {
process: 0,
omit: 0
};
let requestcounter = 0;
let responsecounter = 0;
let total = 0;
let skipotherrequests = 0;
const [, datePart, timePart] = new Date().toISOString().match(/(\d{4}\-\d{2}\-\d{2})T(\d{2}:\d{2}:\d{2})/);
const datetimestamp = `${datePart}-${timePart.replace(/:/g, "-")}`;
picker.setAttribute('data-timestamp', datetimestamp);
const files = Array.from(event.detail || event.target.files);
const processList = await Promise.all(files.map(file => checkDicomMime(file)));
processList.sort((prev, next) => {
return prev.fileName > next.fileName;
});
const badlist = processList.filter(({
isBadFile
}) => isBadFile)
.reduce((acc, result) => acc += `<div>${result.fileName}</div>`, '');
const timestamp = picker.dataset.timestamp;
for (let result of processList) {
const file = result.file;
const type = file.type;
if (result.isBadFile) {
let lineitem = statusitem(file, `Skipping file: ${result.fileName}`);
listing.insertAdjacentHTML('beforeend', lineitem);
continue;
}
console.log('sending file', file)
requestcounter = requestcounter + 1;
await sendFile(file, timestamp, requestcounter, total, type);
}
});
function statusitem(file, text) {
return `<div>${text}</div>`;
}
function checkDicomMime(file) {
const fileReader = new FileReader();
return new Promise((resolve, reject) => {
fileReader.readAsArrayBuffer(file);
fileReader.onload = function(event) {
const target = event.target;
const array = new Uint8Array(target.result);
const start = 128
const end = 132;
const str = [...array.slice(128, 132)].map(value => String.fromCharCode(value)).join('');
const result = {
file,
fileName: file.name,
isBadFile: true
}
if (str == "DICM") {
result.isBadFile = false;
}
fileReader.onload = null;
resolve(result);
}
})
}
const sendFile = function(file, timestamp, requestcounter, total, type) {
return new Promise((resolve, reject) => {
const formData = new FormData();
formData.set('timestamp', timestamp);
formData.set('counter', requestcounter);
formData.set('total', total);
formData.set('type', type);
formData.set('webkitpath', file.webkitRelativePath);
formData.set('file', file);
const request = new XMLHttpRequest();
request.responseType = 'json';
request.onload = function() {
if (request.readyState === request.DONE) {
resolve();
}
}
})
}
function createInvalidFile() {
const data = [new Uint8Array(Array(132).fill(0))]
const file = new File(data, 'invalid-file.txt',{
type: "text/plain"
});
return file;
}
function createValidFile() {
const data = [new Uint8Array(Array(128).fill(0)), new Uint8Array([68, 73, 67, 77])]
const file = new File(data, 'valid-file.txt', {
type: "text/plain"
});
return file;
}
button.addEventListener("click", event => {
const customEvent = new CustomEvent('change', {
detail: [createInvalidFile(), createValidFile()]
});
picker.dispatchEvent(customEvent);
})
<input id="file" type="file" multiple>
<div id="listing"></div>
<button id="button">Send test files</button>
推荐阅读
- swift - 类似承诺的方法
- airflow - 将标记设置为成功并强制所有下游任务运行
- javascript - 如果单击发送按钮,如何防止 android 键盘隐藏 - JavaScript
- python - python中类方法中的对象类型错误
- spring - 如何更改端口 hawtio,我运行一个带有弹簧骆驼休息和其他的 megazord
- sql - 我应该使用特定的 SQL 语法为 SAP 中的 ABAP 的 AutoID 编写 SQL 吗?
- c++ - Q_PROPERTY 未将值设置为通过 qml 上的模型给出的值
- arrays - 用于匹配特定字符的正则表达式
- python - 由于“TypeError: A Future or coroutine is required”,无法在 AWS Lambda 上发出 aiohttp 请求
- python - 减去 DataFrame 列中的元素