为了账号安全,请及时绑定邮箱和手机立即绑定

异步函数 JS 的问题。我可以使用承诺吗?

异步函数 JS 的问题。我可以使用承诺吗?

慕斯王 2022-08-27 13:50:09
我有一些代码,我认为现在不能正常工作,因为我添加了一些东西来使用一些JS来获取MIME类型(真正的MIME类型)。它调用checkDicomMime(file),它异步读取要上传的文件,并确定MIME类型是否与我所查找的内容匹配。我认为MIME类型检测正在工作,但是由于读取文件需要时间,因此我认为其余代码在完成读取MIME类型之前执行。以前,我只是检查文件扩展名,这是同步完成的,因此函数中的“reader.onload = function (evt) {”块中的变量是内联设置的。现在,它调用该函数,并且该函数正确检测 MIME 类型,但看起来调用函数已完成,其余代码在 MIME TYPE 检测完成之前执行,因此在 MIME TYPE 检测完成之前,它会发布列表中每个文件的表单。总数 = counts.process 现在为零,而不是要处理的文件总数,因此 count 和 files.process 和 badfile 要么不被更改,要么仅在所有文件都发布后才更改。我检查了一些调试,看起来它们是在发送文件后设置的。此外,另一篇SO帖子讨论了如何仅读取检测MIME类型的必要字节数,而不是读取整个文件。不知道该怎么做。我在这里得到了DICOM检查功能:检查Dicom这里有一些关于使用JS的MIME类型检测的讨论:如何在上传前用javascript检查文件MIME类型?
查看完整描述

2 回答

?
撒科打诨

TA贡献1934条经验 获得超2个赞

已经接受了答案,但发布了修改后的代码,现在似乎工作得很好。如果您实际上有一个包含某些.dcm文件的文件夹(带或不带文件扩展名),则应排除任何不是真正的.dcm文件的内容,并且可以扩展到其他类型的文件,如我引用的其他帖子中所述。


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


用于检测 MIME 客户端的 GitHub 库


此外,如果您运行代码段,它将使用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>  


查看完整回答
反对 回复 2022-08-27
?
牧羊人nacy

TA贡献1862条经验 获得超7个赞

现在,它调用该函数,并且该函数正确检测 MIME 类型,但看起来调用函数已完成,其余代码在 MIME TYPE 检测完成之前执行,因此在 MIME TYPE 检测完成之前,它会发布列表中每个文件的表单。


您可以更改为承诺并等待所有文件都检查完毕。checkDicomMime


然后,您可以继续在循环中处理它们,并像您一样发送有效的它们。


当然,这需要一些代码重构。



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>


查看完整回答
反对 回复 2022-08-27
  • 2 回答
  • 0 关注
  • 100 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信