Как я могу получить несколько файлов для загрузки на сервер со страницы Javascript без пропуска? - PullRequest
0 голосов
/ 06 декабря 2018

Я работаю над исследовательским экспериментом, который использует getUserMedia, реализованный в Recorder.js, для записи WAV-файлов с микрофона пользователя и XMLHttpRequest для их загрузки на сервер.Каждый файл длится около 3 секунд, всего 36 файлов.Файлы записываются один за другим и отправляются на сервер сразу после их записи.

Проблема, с которой я сталкиваюсь, заключается в том, что не все файлы попадают на сервер.Видимо скрипт или php скрипт не в состоянии отследить все запросы подряд.Как я могу убедиться, что я получаю все файлы?Это важные данные исследований, поэтому мне нужны все записи.

Вот код, который отправляет файлы на сервер.Аудиоданные представляют собой BLOB-объекты:

var filename = subjectID + item__number;    
xhr.onload=function(e) {
    if(this.readyState === 4) {
    console.log("Server returned: ",e.target.responseText);
    }
};
var fd=new FormData();
fd.append("audio_data",blob, filename);
xhr.open("POST","upload_wav.php",true);
xhr.send(fd);

И это файл php на стороне сервера:

print_r($_FILES);     
$input = $_FILES['audio_data']['tmp_name'];
$output = "audio/".$_FILES['audio_data']['name'].".wav";
move_uploaded_file($input, $output)

Этот способ действий в основном скопирован с этого сайта: Использование Recorder.js для захвата аудио WAV в HTML5 и загрузки его на ваш сервер или загрузки локально

Я уже пытался заставить XMLHttpRequest ждать, используя

   while (xhr.readyState != 4) 
   { 
     console.log("Waiting for server...")
   }

Это простовызывал зависание страницы.

Было бы лучше использовать ajax, чем XMLHttp Request?Что я могу сделать, чтобы убедиться, что все файлы загружены?Я довольно новичок в Javascript, поэтому примеры кода приветствуются.

1 Ответ

0 голосов
/ 07 декабря 2018

Я понятия не имею, как выглядит ваша архитектура, но вот потенциальное решение, которое поможет решить вашу проблему.

В решении используется API Web Worker для выгрузки загрузки файла в подпроцесс.Это делается с помощью рабочего интерфейса этого API.Этот подход будет работать, потому что нет единого потока основного процесса - веб-работники работают в своих собственных процессах.

Используя этот подход, мы делаем три основных вещи:

  1. создать нового работника, передавая сценарий для выполнения
  2. передавать сообщения работнику, чтобы рабочий мог работать с
  3. передавать сообщения обратно в основной процесс для обновления статуса / ответов / преобразования разрешенных данных /и т. д.

Ниже приведен подробный код, который поможет вам понять, что и где происходит.

Это основной файл JavaScript (script.js)

// Create a sub process to handle the file uploads
///// STEP 1: create a worker and execute the worker.js file immediately
let worker = new Worker('worker.js');
// Ficticious upload count for demonstration
let uploadCount = 12;
// repeatedly build and send files every 700ms 
// This is repeated until uplaodCount == 0
let builder = setInterval(buildDetails, 700);

// Recieve message from the sub-process and pipe them to the view
///// STEP 2: listen for messages from the worker and do something with them
worker.onmessage = e => {
  let p = document.createElement('pre');
  // e.data represents the message data sent from the sub-process
  p.innerText = e.data;
  document.body.appendChild(p);
};

/**
 * Sort of a mock to build up your BLOB (fake here of-course)
 * 
 * Post the data needed for the FormData() to the worker to handle.
 */ 
function buildDetails() {
  let filename = 'subject1234';
  let blob = new Blob(['1234']);
  ///// STEP 3: Send a message to the worker with file details
  worker.postMessage({
    name: "audio_data",
    blob: blob,
    filename: filename
  });
  // Decrease the count
  uploadCount--;
  // if count is zero (== false) stop the fake process
  if (!uploadCount) clearInterval(builder);
}

Это файл JavaScript подпроцесса (worker.js)

// IGNORE the 'fetch_mock.js' import that is only here to avoid having to stand up a server
// FormDataPolyFill.js is needed in browsers that don't yet support FormData() in workers
importScripts('FormDataPolyFill.js', 'fetch_mock.js');
// RXJS provides a full suite of asynchronous capabilities based around Reactive Programming (nothing to do with ReactJS);
// The need for your use case is that there are guarantees that the stream of inputs will all be processed
importScripts('https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.3.3/rxjs.umd.js');

// We create a "Subject" that acts as a vessel for our files to upload
let forms = new rxjs.Subject();
// This says "every time the forms Subject is updated, run the postfile function and send the next item from the stream"
forms.subscribe(postFile);

// Listen for messages from the main process and run doIt each time a message is recieved
onmessage = doIt;

/**
 * Takes an event object containing the message
 * 
 * The message is presumably the file details
 */ 
function doIt(e) {
  var fd = new FormData();
  // e.data represents our details object with three properties
  fd.append(e.data.name, e.data.blob, e.data.filename);
  // Now, place this FormData object into our stream of them so it can be processed
  forms.next(fd);
}
// Instead of using XHR, this uses the newer fetch() API based upon Promises
// https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
function postFile(fd) {
  // Post the file to the server (This is blocked in fetch_mock.js and doesn't go anywhere)
  fetch('fake', {
      method: 'post',
      body: fd,
    })
    .then((fd) => {
      // After the XHR request is complete, 'Then' post a message back to the main thread (If there is a need);
      postMessage("sent: " + JSON.stringify(fd));
    });
}

Так как он не будет работать в stackoverflow, я создал планзатор, чтобы вы могли запустить этот пример: http://plnkr.co/edit/kFY6gcYq627PZOATXOnk

Если все это кажется сложным, вы представили сложную проблему, которую нужно решить.: -)

Надеюсь, это поможет.

...