簡體   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