Получить байт позиции во время цикла загрузки

Я работаю над функцией, которая будет записывать данные на удаленный сервер в куски, используя сторонний API. Через некоторую помощь в Qaru я смог выполнить это, где он теперь работает, как ожидалось. Проблема в том, что я могу получить только один 16kb кусок для записи, поскольку мне нужно будет продвинуть pos, где записаны следующие байты.

Начальная запись начинается с 0 достаточно легко. Из-за моей незнакомости с этим, однако, я не уверен, что следующий pos должен быть только 16 или что. Если это помогает, вызов API writeFileChunk() принимает 3 параметра, путь к файлу (str), pos (int64) и данные (строка с кодировкой 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);
            }
        })()
    };

Ответ 1

Текущая позиция для файла на стороне клиента будет представлена ​​этой линией, или, более конкретно, вторым аргументом на этапе предварительного инкремента:

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

хотя в этом случае он продвигается (count++), прежде чем вы сможете его повторно использовать, если вам нужна фактическая позиция (ниже как pos), вы можете извлечь ее, просто переписав строку на:

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

Здесь каждое обновление позиции увеличит 16kb, так как это размер блока. Для прогресса он рассчитывается pos / fileSize * 100. Это, конечно, предполагает использование размера незарегистрированного буфера.

Единственный частный случай - последний фрагмент, но когда больше нет кусков, оставшихся для чтения, позиция должна быть равна длине файла (fileSize), поэтому она должна быть довольно прямой.

При возврате вызова ajax сервер должен иметь одинаковую позицию, если что-то не пошло не так (соединение, изменение доступа к записи, полный диск и т.д.).

Ответ 2

Вы можете использовать API Filereader для чтения кусков и отправки его на ваш удаленный сервер.

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