JS - TEXT 文件 GZIP'ed 上传(修复:错误的回调)

JS - TEXT File GZIP'ed for UPLOAD (fixed: wrong callback)

浏览文件、上传和 GZIP 压缩都可以正常工作,但 GZIP 压缩后的 NewFile 会被忽略并发送原始文件 [i]。

我没有将文件 [i] 对象传递给 Upload(),而是使用 GZIP 压缩后的数据创建了一个新的 File object,然后将其传递给 Upload(),这就是“ <<< HERE <<<" 在代码下方注释:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Demo</title>
</head>
<body>
<p>Send new/updated text to the server:</p>
<form id="upload" action="/?entity" method="POST" 
      enctype="multipart/form-data">
<input type="hidden" id="MAX_FILE_SIZE" name="MAX_FILE_SIZE" value="3000000" />
<div>
 <input type="file" id="fileselect" name="fileselect[]" multiple="multiple" />
 <div id="filedrag">Drop your TEXT<br>file(s) here!</div>
</div>
<div id="submitbutton">
 <button type="submit">Upload Files</button>
</div>
</form>

<table><tr><td>
<fieldset><legend>Server Reply</legend><div id="srvreply"></div></fieldset>
</td><td>
<fieldset><legend>Network Transfer</legend><div id="progress"></div></fieldset>
</td><td>
<fieldset><legend>Dropped Files</legend><div id="doclist"><p></p></div></fieldset>
</td></tr></table>

<script src="https://raw.githubusercontent.com/imaya/zlib.js/master/bin/zlib_and_gzip.dev.min.js"></script>
<script>
// -----------------------------------------------------------------------------
function $id(id) {return document.getElementById(id)}
// -----------------------------------------------------------------------------
function Output(msg)
{var m = $id("doclist"); m.style.visibility="visible"; 
 m.innerHTML += msg; m.scrollTop = m.scrollHeight;
}
// -----------------------------------------------------------------------------
function toUTF8Array(str) { // JS strings are UTF-16 (bloated)
 var utf8 = [];
 for(var i=0; i<str.length; i++) 
 {var charcode = str.charCodeAt(i);
  if(charcode < 0x80) utf8.push(charcode); 
  else 
  if(charcode < 0x800)
     utf8.push(0xc0 | (charcode >> 6), 
               0x80 | (charcode & 0x3f)); 
  else 
  if(charcode < 0xd800 || charcode >= 0xe000)
     utf8.push(0xe0 | (charcode >> 12), 
               0x80 | ((charcode>>6) & 0x3f), 
               0x80 | (charcode & 0x3f));
  else // surrogate pair
  { i++;
    // UTF-16 encodes 0x10000-0x10FFFF by
    // subtracting 0x10000 and splitting the
    // 20 bits of 0x0-0xFFFFF into two halves
    charcode = 0x10000 + (((charcode & 0x3ff)<<10)
             | (str.charCodeAt(i) & 0x3ff))
    utf8.push(0xf0 | (charcode >>18), 
              0x80 | ((charcode>>12) & 0x3f), 
              0x80 | ((charcode>>6) & 0x3f), 
              0x80 | (charcode & 0x3f));
  }
 }
 return utf8;
}
// =============================================================================
// Drag & Drop, UI, JPG & TEXT files
// -----------------------------------------------------------------------------
(function(){
// -----------------------------------------------------------------------------
function sleep(ms) {return new Promise(resolve => setTimeout(resolve, ms));}
// -----------------------------------------------------------------------------
// Loop to process all files
// -----------------------------------------------------------------------------
function FileDragHover(e)
{e.stopPropagation();
 e.preventDefault();
 e.target.className = (e.type == "dragover" ? "hover" : "");
}

function FileSelectHandler(e)
{FileDragHover(e); // cancel event and hover styling
 var files = e.target.files || e.dataTransfer.files; // fetch FileList
 for(var i = 0; f = files[i]; i++) // process and upload all files
   UploadFile(ParseFile(f));   // <<<<<<<<<< HERE <<<<<<<<<<
}
// -----------------------------------------------------------------------------
// Read Dropped Files, Output File Name and Size
// -----------------------------------------------------------------------------
function ParseFile(file)
{  var newFile = file;
    if(!file.type.indexOf("text")) // "text/plain"
    {   var reader = new FileReader();
        reader.onload = function(e) 
       {  Output(file.name + " " 
           + Math.round((file.size/1024) * 100) / 100 + " KB<br>");

           // compress payload with GZIP
           var gzip = new Zlib.Gzip(file);
         var comp = gzip.compress();

           Output("read " 
               + Math.round((file.size/1024) * 100) / 100
               + " KB, comp " 
               + + Math.round((comp.length/1024) * 100) / 100
               + " KB<br>");

         newFile = new File([new Uint8Array(comp)], file.name, 
                            {type:'text/plain'});
        }
        reader.readAsText(file);
        return newFile;
    } 
}
// -----------------------------------------------------------------------------
// Send Files to Server
// -----------------------------------------------------------------------------
function UploadFile(file)
{   // location.host
    var xhr = new XMLHttpRequest();/*
    Output("xhr.upload: " + xhr.upload + "<br>file.type: " + file.type 
          + "<br>file.size: " + file.size + "<br>");*/
    if(xhr.upload 
    && (file.type == "text/plain" || file.type == "image/jpeg"
       || file.type.indexOf("ms-excel") > 0 
      || file.type.indexOf("spreadsheet") > 0)
    && file.size <= $id("MAX_FILE_SIZE").value) 
    {   // create progress bar
        var o = $id("progress");
        var progress = o.appendChild(document.createElement("p"));
        progress.appendChild(document.createTextNode(file.name + ": 0%"));
        o.scrollTop = o.scrollHeight;

        xhr.upload.addEventListener("progress", function(e) // progress bar
        {   var pc = parseInt(e.loaded / e.total * 100);
            var txt = progress.innerHTML;
            progress.innerHTML = txt.slice(0, txt.indexOf(":") + 2) + pc + "%";
        }, false);

        // file received/failed
        // 0: request not initialized
        // 1: server connection established
        // 2: request received
        // 3: processing request
        // 4: request finished and response is ready 
        xhr.onreadystatechange = function(e) {
            if(xhr.readyState == 0)
                $id("srvreply").innerHTML += "<br>- preparing request";

            if(xhr.readyState == 1)
                $id("srvreply").innerHTML += "<br>- server connected";

            if(xhr.readyState == 2)
                $id("srvreply").innerHTML += "<br>- server got request";

            if(xhr.readyState == 3)
                $id("srvreply").innerHTML += "<br>- server processing request";

            if(xhr.readyState == 4)
            {
               // 200: "OK", 403: "Forbidden", 404: "Not Found"
               var msg = (xhr.status == 200 ? "success" : "failure");
                progress.className = msg;
                //$id("srvreply").innerHTML += "<br>- " + xhr.status + " " 
                //                        + xhr.statusText;

                if(xhr.status == 200)
                   $id("srvreply").innerHTML +=  "<br>" + xhr.responseText;

            // force scrolling-down (so users see the latest entries)
                $id("srvreply").scrollTop = $id("srvreply").scrollHeight;
           }
        };
        xhr.open("POST", $id("upload").action, true); // start upload
        xhr.overrideMimeType('text/plain; charset=utf-8');
        xhr.setRequestHeader("XXX-FILENAME", file.name);
        xhr.send(file);
    }
}
// -----------------------------------------------------------------------------
function Init()
{   var fileselect = $id("fileselect"), filedrag = $id("filedrag"),
         submitbutton = $id("submitbutton");
    fileselect.addEventListener("change", FileSelectHandler, false);
    var xhr = new XMLHttpRequest(); // is XHR2 available?
    if(xhr.upload) // file drop
    {   filedrag.addEventListener("dragover", FileDragHover, false);
        filedrag.addEventListener("dragleave", FileDragHover, false);
        filedrag.addEventListener("drop", FileSelectHandler, false);
        filedrag.style.display = "block";
        submitbutton.style.display = "none"; // remove submit button
    }
}
// -----------------------------------------------------------------------------
if(window.File && window.FileList && window.FileReader) { Init(); }
// -----------------------------------------------------------------------------
})();
// -----------------------------------------------------------------------------
</script>
</body>
</html>

但是,Upload() 发送的是原始文件[i] 对象,而不是我新创建的对象。我当然遗漏了一些明显的东西,但我看不到什么。

完整的 HTML/JS 代码已发布,以便人们可以在家进行测试。

GZIP 负载为空,因为源文件在 readAsText(file) 之后尚未加载:

输出:

  • 阅读前:EMPTY
  • 阅读后:正在加载
  • 加载结束:完成 <<<<<< onload[END]

qcm.txt 2.99 KB(读取 2.99 KB,压缩 0.8 KB)<<<<<< 可信压缩

工作代码:

var rdSt = {};
rdSt[FileReader.EMPTY]   = 'EMPTY';
rdSt[FileReader.LOADING] = 'LOADING';
rdSt[FileReader.DONE]    = 'DONE';

function ParseFile(file)
{
  if(!file.type.indexOf("text")) // "text/plain"
  { var reader = new FileReader();
    reader.onloadend = function(e)
    { var data = e.target.result;
      var gzip = new Zlib.Gzip(data);
      var comp = gzip.compress();
      var newFile = new File([new Uint8Array(comp)], file.name,
                            {type:'text/plain'});

      Output('- onloadend  : '+rdSt[reader.readyState]+"<br>");
      Output(file.name +" "+ Math.round((file.size/1024)*100)/100
      + " KB<br>"
      + "(read " + Math.round((file.size/1024)*100)/100
      + " KB, comp "
      + Math.round((comp.length/1024)*100)/100 +" KB)<br>");

      UploadFile(newFile); // <<<<< MUST be done HERE!
    }

    Output('- before read: '+rdSt[reader.readyState]+"<br>");
    reader.readAsText(file);
    Output('- after read : '+rdSt[reader.readyState]+"<br>");
  }
}

所以回调是强制性的 - 我只是没有合适的回调(onload 而不是 onloadend)。

显然,newFile 始终未压缩,因为压缩甚至没有开始! ParseFile() 不能 return newFile 因为压缩仅以 onloadend() 结束(回调现在调用 UploadFile())。

作为最新练习,zlib.js "Gzip" 压缩不符合 HTTP GZIP 标准(或者我误用了另一个 JS 库)。

https://www.javascripture.com/ 帮我找到了相关的 Javascript 文档(稀缺资源)。

运气好的话,这种痛苦的辛苦工作会帮助别人...