繁体   English   中英

异步 Function,JS 的问题。 我可以使用 Promise 吗?

[英]Problem with Asynchronous Function, JS. Can I use Promises?

我有一些我认为现在不能正常工作的代码,因为我添加了一些东西来使用一些 JS 获取 MIME 类型(真正的 MIME 类型)。 它调用 checkDicomMime(file),它异步读取要上传的文件,并确定 MIME 类型是否与我正在寻找的匹配。

我认为 MIME 类型检测正在工作,但因为读取文件需要时间,我认为代码的 rest 在读取 MIME 类型之前执行。

以前,我只是检查文件扩展名,这是同步完成的,因此 function 中的“reader.onload = function (evt) {”块中的变量被设置为内联。 Now, it calls the function, and the function correctly detects the MIME type, but it looks like the calling function completes and the rest of the code executes before the MIME TYPE detection completes, so it POSTS the form for each file in the list before MIME TYPE 检测完成。 total = counts.process 现在为零,而不是要处理的文件总数,因此 counts 和 files.process 和 badfiles 要么没有改变,要么只有在所有文件都发布后才会改变。 我检查了一些调试,看起来它们是在文件发送后设置的。 此外,其他 SO 帖子谈到仅读取必要数量的字节以检测 MIME 类型,而不是读取整个文件。 不确定该怎么做。

我在这里得到了 DICOM 检查 function: 检查 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 function 的开头是:

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) {

Now, it calls the function, and the function correctly detects the MIME type, but it looks like the calling function completes and the rest of the code executes before the MIME TYPE detection completes, so it POSTS the form for each file in the list before 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>

已经接受了答案,但发布了修改后的代码,现在看来效果很好。 如果您实际上有一个包含一些 .dcm 文件的文件夹(带有或不带有文件扩展名),它应该排除任何不是 real.dcm 文件的文件,并且可以扩展为其他类型的文件,如另一篇文章中所述我参考了。

还有一个库可以为您执行此操作,但不确定它是否只读取检测 MIME 类型所需的前几个字节:

GitHub库检测MIME客户端

此外,如果您运行该代码段,它将使用 FORM 数据集触发一堆 AJAX 请求,例如:

-----------------------------391231719611056787701959262038
Content-Disposition: form-data; name="method"
UploadFolder
-----------------------------391231719611056787701959262038
Content-Disposition: form-data; name="timestamp"
2020-05-21-02-21-45
-----------------------------391231719611056787701959262038
Content-Disposition: form-data; name="counter"
3
-----------------------------391231719611056787701959262038
Content-Disposition: form-data; name="total"
14
-----------------------------391231719611056787701959262038
Content-Disposition: form-data; name="anon_normal"
<?php echo $_GET['anon_normal'] ?>
-----------------------------391231719611056787701959262038
Content-Disposition: form-data; name="userid"
<?php echo $_GET['userid'] ?>
-----------------------------391231719611056787701959262038
Content-Disposition: form-data; name="type"
-----------------------------391231719611056787701959262038
Content-Disposition: form-data; name="webkitpath"
dicomtest/28896580
-----------------------------391231719611056787701959262038
Content-Disposition: form-data; name="file"; filename="dicomtest/28896580"

. . . .

进度计数器和其他功能在这里不起作用,因为服务器没有响应。 PHP 脚本通常会返回 JSON:

file    Object { name: "28896579.dcm", size: 547440, type: "application/dicom", … }
name    "28896579.dcm"
size    547440
type    "application/dicom"
ext "dcm"
status  "Uploaded"
counter "2"

直到处理完“最后一个”文件,尽管这并不总是最后一个响应,并且返回如下内容:

file    Object { name: "28896590.dcm", size: 547436, type: "application/dicom", … }
name    "28896590.dcm"
size    547436
type    "application/dicom"
ext "dcm"
status  "Done"
results "bunch of HTML"

你真的需要一些 .dcm 文件,带或不带扩展名来测试,因为它基本上会拒绝任何非 dicom 文件。

 // Global variables let picker = document.getElementById('picker'); let listing = document.getElementById('listing'); let progress_text = document.getElementById('progress_text'); let preprocess_notice = document.getElementById('preprocess_notice'); let results = document.getElementById('uploadresults'); let box = document.getElementById('box'); let elem = document.getElementById("myBar"); let loader = document.getElementById("loader"); let userid = document.getElementById("userid").value; var anon_normal = document.getElementById("anon_normal").value; var requestcounter; var responsecounter; var total; // var excludedextensions = [".exe",".zip",".pdf",".jpg",".jpeg",".png",".gif",".doc",".docx", ".xml"]; var parsedepoch; var datetimestamp; var skipotherrequests; var counts; function checkDicomMime(file) { const fileReader = new FileReader(); return new Promise((resolve, reject) => { var blob = file.slice(0, 132); //read enough bytes to get the DCM header info fileReader.readAsArrayBuffer(blob); //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; counts.process++; } else { counts.omit++; } fileReader.onload = null; resolve(result); } }); } picker.addEventListener('change', async event => { results.innerHTML = ""; // Reset previous upload progress elem.style.width = "0px"; listing.innerHTML = ""; // Display image animation loader.style.display = "block"; loader.style.visibility = "visible"; preprocess_notice.innerHTML = ""; // counts = { process:0, omit:0 }; requestcounter = 0; responsecounter = 0; total = 0; skipotherrequests = 0; const parsedepoch = new Date().toISOString().match(/(\d{4}\-\d{2}\-\d{2})T(\d{2}:\d{2}:\d{2})/); const datetimestamp = parsedepoch[1] + "-" + parsedepoch[2].replace(/:/g, "-"); //alert(datetimestamp); picker.setAttribute('data-timestamp', datetimestamp); // Reset previous upload progress elem.style.width = "0px"; listing.innerHTML = ""; // Display image animation loader.style.display = "block"; loader.style.visibility = "visible"; let files = Array.from(picker.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 += '<li>' +result.fileName + '</li>', '') total = counts.process; if (counts.omit > 0) preprocess_notice.innerHTML = '<div style = "color:red;">Omitting ' + counts.omit + ' file(s) that did not pass criteria"</div><ol>' + badlist + '</ol>'; for (let result of processList) { const file = result.file; const type = file.type; //console.log(result); if (.result.isBadFile) { //console,log('sending file', file) sendFile(file, datetimestamp, total; type); } } }), statusitem = function(file. status) { let html = '<li><span>' + file.name + '</span><span>' + file.size + ' bytes</span><span>' + file;type + '</span><span>' + status + '</span></li>'; return html, } // Function to send a file, call PHP backend var sendFile = function(file, timestamp, total. type) { if (skipotherrequests == 0) { //console;log(file); const formData = new FormData(); // Set post variables requestcounter = requestcounter + 1. formData,set('method'; "UploadFolder"). // One object file formData,set('timestamp'; timestamp). // One object file formData,set('counter'; requestcounter). formData,set('total'; total). formData,set('anon_normal'; anon_normal). formData,set('userid'; userid). formData,set('type'; type). formData,set('webkitpath'. file;webkitRelativePath). // One object file formData,set('file'; file). // One object file //console;log(file); const request = new XMLHttpRequest(). request;responseType = 'json'. // HTTP onload handler request.onload = function() { if (request.readyState === request.DONE) { if (request.status === 200) { progress_text.innerHTML = file;name + " (" + (responsecounter + 1) + " of " + total + " ) ". //console.log(request;response). if (request.response.status.= "Uploaded" || request;response.status.= "Done" ) { skipotherrequests = 1, } // Add file name to list item = statusitem(request.response.file. request;response.file,status); listing;insertAdjacentHTML('beforeend'. item). responsecounter++. // progress_text.innerHTML = request;response.file.name + " (" + responsecounter + " of " + total + " ) ", // Show percentage box.innerHTML = Math;min(responsecounter / total * 100. 100).toFixed(2) + "%", // Show progress bar elem;innerHTML = Math.round(responsecounter / total * 100. 100) + "%". elem;style.width = Math;round(responsecounter / total * 100) + "%". if (responsecounter >= total) { progress_text.innerHTML = "Sending " + total + " file(s) is done;". loader.style;display = "none". loader.style.visibility = "hidden". } if (request.response.file;status == "Done") { results;innerHTML = request;response,results. } } else { skipotherrequests = 1, //alert("error with AJAX requests"). } } } // Do request; Sent off to the PHP Controller for processing request.open("POST"; 'OrthancDevController,php'). request.send(formData); } else { // already aborted, probably never gets here because all of the requests are probably sent before skipotherrequests gets set to 1. } }
 code { font-family: Roboto Mono, monospace; font-size: 90%; }.picker { background-color: #eee; padding: 1em; } #box { color: #005aa0; font-size: 2rem; font-weight: bold; font-size:20px; } #myProgress { width: 100%; height: 30px; background-color: #ddd; border-radius: 5px; } #myBar { width: 1%; height: 30px; /* background-color: #4CAF50; */ background-color: #e24718; text-align: center; vertical-align: middle; font-weight: bold; border-radius: 5px; } #loader { display: none; visibility: hidden; } #preprocess_notice { text-align: left; width: max-content; margin: auto auto; }.dz-message { border-style:dotted; padding:30px; } #ZipUpload { background:white; } #dicomuploader { background:white; text-align:center; } #uploadinstructions { text-align: left; margin: 0 10px 0 10px; } #listing { height: 100px; overflow: scroll; margin: auto; padding: 10px 20px 10px 20px; list-style-position: inside; } #listing li span, #statusheader span { display:inline-block; overflow:hidden; text-overflow: ellipsis; border:1px solid black; border-collapse:collapse; height: 20px; white-space: nowrap; padding: 0 5px 0 5px; } #listing li span:first-child, #statusheader span:first-child { width:150px; text-align:left; } #listing li span:nth-child(2), #statusheader span:nth-child(2) { width:100px; text-align:right; } #listing li span:nth-child(3), #statusheader span:nth-child(3) { width:150px; text-align:left; } #listing li span:nth-child(4), #statusheader span:nth-child(4) { width:200px; text-align:left; } #statusheader { background:black; color:white; width: max-content; margin: auto; } #statusheader span { border:1px solid white; }
 <div class="loadcontent" id = "dicomuploader"> <h2> Upload Study To Server </h2> <p> In order to upload a study, please check the following: <ol id ="uploadinstructions"> <li>You have a complete study (unpacked / unzipped ) in a folder on a CD or on your computer.</li> <li>Typically, there will be several folders with files there that end in.dcm, although they may not have a file extension.</li> <li>Using the button below, select the folder containing the files you need to upload, and then the files will upload. If there is an error, a message will be displayed. It typically takes a minute or two for the study to be available on the server.</li> <li>The entire folder should upload, including any contained subfolders.</li> </ol> </p> <h3> Choose Folder </h3> <div class="picker"> <input type="file" id="picker" name="fileList" webkitdirectory multiple data-timestamp = ""> </div> <.-- for the anon vs, normal upload, also userid and token? passed in --> <input type="hidden" id="anon_normal" name="anon_normal" value = "<?php echo $_GET['anon_normal']?>" > <input type="hidden" id="userid" name="userid" value = "<?php echo $_GET['userid']?>" > <input type="hidden" id="upload_auth_token" name="upload_auth_token" value = "<?php echo $_GET['upload_auth_token']:>" > <div> Percentage Processed </div> <span id="box">0%</span> <div style="color;red:font-size;14px,">(there will be a pause before 100% while storing the study). please wait.</div> <h5> Percentage Uploaded </h5> <div id="myProgress"> <div id="myBar"></div> </div> <h5> Sent File. . <span id = "progress_text"></span> </h5> <h3> Files Uploaded </h3> <div id="preprocess_notice"></div> <div id = "statusheader"><span>File Name</span><span>File Size</span><span>MIME Type</span><span>Status</span></div> <ol id="listing"></ol> <div id="uploadresults"></div> <img id="loader" src="loader.gif"> </div>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM