在上传循环期间获取字节位置

时间:2016-12-08 12:33:33

标签: javascript jquery ajax filereader arraybuffer

我正在开发一个函数,它将使用第三方API以块的形式将数据写入远程服务器。通过Stack Overflow的一些帮助,我能够实现这一目标,现在它正在按预期工作。问题是我只能得到一个16kb的块来写,因为我需要提前写入下一个字节的pos

初始写入很容易从0开始。由于我对此不熟悉,我不确定下一个pos应该只是16还是什么。如果有帮助,API调用writeFileChunk()需要3个参数,即filepath(str),pos(int64)和data(base64编码的字符串)。

    reader.onload = function(evt)
    {
        // Get SERVER_ID from URL
        var server_id = getUrlParameter('id');

        $("#upload_status").text('Uploading File...');
        $("#upload_progress").progressbar('value', 0);

        var chunkSize = 16<<10;
        var buffer = evt.target.result;
        var fileSize = buffer.byteLength;
        var segments = Math.ceil(fileSize / chunkSize); // How many segments do we need to divide into for upload
        var count = 0;

        // start the file upload
        (function upload()
        {
            var segSize = Math.min(chunkSize, fileSize - count * chunkSize);

            if (segSize > 0)
            {
                $("#upload_progress").progressbar('value', (count / segments));

                var chunk = new Uint8Array(buffer, count++ * chunkSize, segSize); // get a chunk
                var chunkEncoded = btoa(String.fromCharCode.apply(null, chunk));

                // Send Chunk data to server
                $.ajax({
                    type: "POST",
                    url: "filemanagerHandler.php",
                    data: { 'action': 'writeFileChunk', 'server_id': server_id, 'filepath': filepath, 'pos': 0, 'chunk': chunkEncoded },
                    dataType: 'json',
                    success: function(data)
                    {
                        console.log(data);
                        setTimeout(upload, 100);
                    },
                    error: function(XMLHttpRequest, textStatus, errorThrown)
                    {
                        alert("Status: " + textStatus); alert("Error: " + errorThrown); alert("Message: " + XMLHttpRequest.responseText);
                    }
                });
            }
            else
            {
                $("#upload_status").text('Finished!');
                $("#upload_progress").progressbar('value', 100);

                getDirectoryListing(curDirectory);
            }
        })()
    };

2 个答案:

答案 0 :(得分:8)

客户端文件的当前位置将由此行表示,或者更具体地说是在增量前步骤中的第二个参数:

var chunk = new Uint8Array(buffer, count++ * chunkSize, segSize);

但是,在这种情况下,它会在您重复使用之前前进(count++),因此如果您需要实际位置(在pos下方),您可以通过简单地将该行重写为:< / p>

var pos = count++ * chunkSize;   // here chunkSize = 16kb
var chunk = new Uint8Array(buffer, pos, segSize);

此处每个位置更新将增加16kb,因为这是块大小。对于进度,则计算pos / fileSize * 100。这当然假设使用未编码的缓冲区大小。

唯一的特殊情况是最后一个块,但是当没有剩余的块要读取时,位置应该等于文件长度(fileSize)所以它应该非常直接。

当ajax调用返回时,服务器应该具有相同的位置,除非出现问题(连接,写访问更改,磁盘已满等)。

答案 1 :(得分:2)

您可以使用Filereader API读取块并将其发送到远程服务器。

<强> HTML

<input type="file" id="files" name="file" /> Read bytes: 
<span class="readBytesButtons">
  <button>Read entire file in chuncks</button>
</span>

<强>的Javascript

  // Post data to your server.
  function postChunk(obj) {
    var url = "https://your.remote.server";
    return new Promise(function(resolve, reject) {
        var xhr = new XMLHttpRequest();
        xhr.open('post', url, true);
        xhr.responseType = 'json';
        xhr.onload = function() {
            var status = xhr.status;
            if (status == 200) {
                resolve(xhr.response);
            } else {
                reject(status);
            }
        };
        var params = "";
        // check that obj has the proper keys and create the url parameters
          if (obj.hasOwnProperty(action) && obj.hasOwnProperty(server_id) && obj.hasOwnProperty(filepath) && obj.hasOwnProperty(pos) && obj.hasOwnProperty(chunk)) {
            params += "action="+obj[action]+"&server_id="+obj[server_id]+"&filepath="+obj[filepath]+"&pos="+obj[pos]+"&chunk="+obj[chunk];
          }
        if(params.length>0) {
        xhr.send(params);
        } else {
        alert('Error');
        }       
    });
  }
// add chunk to "obj" object and post it to server
function addChunk(reader,obj,divID) {
    reader.onloadend = function(evt) {
      if (evt.target.readyState == FileReader.DONE) { // DONE == 2
        obj.chunk = evt.target.result;
      console.log(obj);
            document.getElementById(divID).textContent += 
            ['Sending bytes: ', obj.pos*16000, ' - ', ((obj.pos*16000)+(obj.pos+1)*obj.chunk.length),
             '\n'].join('');
      // post data to server
      postChunk(obj).then(function(data) {  
          if(data!=="" && data!==null && typeof data!=="undefined") {
            // chunk was sent successfully
            document.getElementById(divID).textContent += 
            ['Sent bytes: ', obj.pos*16000, ' - ', ((obj.pos*16000)+(obj.pos+1)*obj.chunk.length),'\n'].join('');
          } else {
          alert('Error! Empty response');   
          }
        }, function(status) {
          alert('Resolve Error');
        });
      }
    };
}
 // read and send Chunk
 function readChunk() {
    var files = document.getElementById('files').files;
    if (!files.length) {
      alert('Please select a file!');
      return;
    }
    var file = files[0];
    var size = parseInt(file.size);
    var chunkSize = 16000;
    var chunks = Math.ceil(size/chunkSize);
    var start,stop = 0;
    var blob = [];
    for(i=0;i<chunks;i++) {
    start = i*chunkSize;
    stop = (i+1)*chunkSize-1;
    if(i==(chunks-1)) {
    stop = size;
    } 
    var reader = new FileReader();
    blob = file.slice(start, stop);
    reader.readAsBinaryString(blob);
    var obj = {action: 'writeFileChunk', server_id: 'sid', filepath: 'path', pos: i, chunk: ""};   
    var div = document.createElement('div');
    div.id = "bytes"+i;
    document.body.appendChild(div);
    addChunk(reader,obj,div.id);       
    }  
 }
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
  console.log(' Great success! All the File APIs are supported.');
} else {
  alert('The File APIs are not fully supported in this browser.');
}
  document.querySelector('.readBytesButtons').addEventListener('click', function(evt) {
    if (evt.target.tagName.toLowerCase() == 'button') {
      readChunk();
    }
  }, false);

您可以在此Fiddle

中查看此示例