如何在不跳过的情况下从 Javascript 页面获取多个文件上传到服务器?
How can I get multiple files to upload to the server from a Javascript page without skipping?
我正在进行一项研究实验,该实验使用 recorder.js 中实现的 getUserMedia 来记录来自用户麦克风的 .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)
这种做法基本是从这个网站抄来的:
Using Recorder.js to capture WAV audio in HTML5 and upload it to your server or download locally
我已经尝试使用
让 XMLHttpRequest 等待
while (xhr.readyState != 4)
{
console.log("Waiting for server...")
}
只是导致页面挂起。
使用 ajax 会比使用 XMLHttp 请求更好吗?我可以做些什么来确保所有文件都上传了吗?我是 Javascript 的新手,所以非常感谢代码示例。
我不知道您的架构是什么样的,但这里有一个可能的解决方案可以解决您的问题。
该解决方案使用 API 的 Web Worker API to off load the file uploading to a sub-process. This is done with the Worker Interface。这种方法会起作用,因为没有主进程的单线程争用 - Web 工作者在自己的进程中工作。
使用这种方法,我们做了三件基本的事情:
- 创建一个新的工人传递一个脚本来执行
- 将消息传递给 worker 以供 worker 处理
- 将消息传回主进程以获得状态 updates/replies/resolved 数据 transformation/etc。
下面对代码进行了大量注释,以帮助您了解发生了什么以及发生了什么。
这是主要的 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));
});
}
由于这不会 运行 在 Whosebug 中,我创建了一个 plunker 以便您可以 运行 这个例子:
http://plnkr.co/edit/kFY6gcYq627PZOATXOnk
如果这一切看起来很复杂,那么您提出了一个需要解决的复杂问题。 :-)
希望对您有所帮助。
我正在进行一项研究实验,该实验使用 recorder.js 中实现的 getUserMedia 来记录来自用户麦克风的 .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)
这种做法基本是从这个网站抄来的: Using Recorder.js to capture WAV audio in HTML5 and upload it to your server or download locally
我已经尝试使用
让 XMLHttpRequest 等待 while (xhr.readyState != 4)
{
console.log("Waiting for server...")
}
只是导致页面挂起。
使用 ajax 会比使用 XMLHttp 请求更好吗?我可以做些什么来确保所有文件都上传了吗?我是 Javascript 的新手,所以非常感谢代码示例。
我不知道您的架构是什么样的,但这里有一个可能的解决方案可以解决您的问题。
该解决方案使用 API 的 Web Worker API to off load the file uploading to a sub-process. This is done with the Worker Interface。这种方法会起作用,因为没有主进程的单线程争用 - Web 工作者在自己的进程中工作。
使用这种方法,我们做了三件基本的事情:
- 创建一个新的工人传递一个脚本来执行
- 将消息传递给 worker 以供 worker 处理
- 将消息传回主进程以获得状态 updates/replies/resolved 数据 transformation/etc。
下面对代码进行了大量注释,以帮助您了解发生了什么以及发生了什么。
这是主要的 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));
});
}
由于这不会 运行 在 Whosebug 中,我创建了一个 plunker 以便您可以 运行 这个例子: http://plnkr.co/edit/kFY6gcYq627PZOATXOnk
如果这一切看起来很复杂,那么您提出了一个需要解决的复杂问题。 :-)
希望对您有所帮助。