提问人:Devin Gilbert 提问时间:11/11/2023 更新时间:11/11/2023 访问量:36
如何使用 JavaScript 将视频 blob 发送到使用 Django 表单的 Django 视图?
How can you use JavaScript to send a video blob to a Django view using a Django form?
问:
简而言之
如何使用 JavaScript 通过 Django 表单将一团视频数据获取到 Django 视图?
背景
我正在构建一个功能来录制网络摄像头视频。我已经成功地弄清楚了这部分。我可以使用 JavaScript 流式传输用户的摄像头和麦克风,录制流,将其转换为 blob,然后能够将 blob 转换为文件。但是,我不想要文件,因为我需要将用户的视频简单地发送到我的后端,并且我不希望用户与视频文件进行交互(我刚刚了解到,出于安全原因,您不能以编程方式为元素分配文件)。这就是为什么我只想发送二进制数据。<input type='file'>
我尝试了什么(以及结果是什么)
- 就像我说的,我尝试在我的 Django 表单中使用 FileField(但由于固有的 Web 安全限制,不起作用)。让用户自行下载和上传文件不符合项目要求。
- 我尝试将 JSONField 用于我的 Django 表单并以 JSON 格式提交二进制文件。也许这仍然有效,但我无法将二进制数据转换为 JSON 格式。到目前为止,我尝试过的所有方法,我的 JSON 中应该具有 blob 的值最终要么是空字符串,要么是像这样的字符串 -> 。
undefined
"data:"
法典
JavaScript的
// This function is what MediaRecorder uses to give data to "recordedChunks"
const handleDataAvailable = function(event) {
if (event.data.size > 0) {
recordedChunks.push(event.data);
} else {
// …
}
}
问题出在下一步(停止录制、创建 blob 并将 blob 获取到元素)。我将展示这个函数的每个变体,并事先解释最终的值是什么:input
base64data
console.log(base64data);
= undefined
const stopCapture = function() {
mediaRecorder.stop();
const blob = new Blob(recordedChunks, {type: "video/webm",});
// Convert the Blob to base64 and then JSON format to submit with form
const reader = new FileReader();
reader.onload = function () {
const base64data = reader.result.split(',')[1];
console.log(base64data);
const jsonData = JSON.stringify({ videoBlob: base64data });
// Set the value of the input element to the base64-encoded blob
jsonInput.value = jsonData;
};
reader.readAsDataURL(blob);
}
console.log(base64data);
= data:
更改为 .const base64data = reader.result.split(',')[1];
const base64data = reader.result;
console.log(base64data);
= 空字符串
//...after blob is created
// Convert the Blob to base64 and then JSON format to submit with form
const reader = new FileReader();
reader.onload = function () {
const arrayBuffer = reader.result;
// Convert array buffer to base64
const base64data = arrayBufferToBase64(arrayBuffer);
// Create a JSON-formatted string with the base64-encoded blob data
const jsonData = JSON.stringify({ videoBlob: base64data });
// Set the value of the hidden input to the JSON representation
blobInput.value = jsonData;
};
reader.readAsArrayBuffer(blob);
}
// Function to convert array buffer to base64
function arrayBufferToBase64(arrayBuffer) {
const uint8Array = new Uint8Array(arrayBuffer);
const binaryString = uint8Array.reduce((acc, byte) => acc + String.fromCharCode(byte), '');
return btoa(binaryString);
}
其他想法
- 也许最好不要使用 Django 表单?如果我这样做,那么我会通过 JavaScript 本身将表单数据提交到 Django 中的单独视图(使用 和 )。但是,我宁愿不这样做,因为我喜欢 Django 表单提供的内置安全性。
formData()
fetch()
- 也许最好先将数据发送到我的 Django 后端(一个单独的视图),然后再以某种方式将其转换为 blob?
Django 表单和视图代码
包括这一点,以防万一对我的整体工作流程有建议,因为我的最终目标是从用户录制视频到我的后端将该视频发送到第三方存储服务。
forms.py
from django import forms
class VidFileUploadForm(forms.Form):
vidBlob = forms.JSONField(widget=forms.HiddenInput)
name = forms.CharField(required=True)
descrip = forms.CharField(required=True)
views.py
class VidDemoTwoView(FormMixin, TemplateView, ProcessFormView):
"""View class for experimental video recording view"""
template_name = 'evaluations/vid_demo2.html'
form_class = VidFileUploadForm
success_url = reverse_lazy('view-vid-demo')
def form_valid(self, form):
vidBlob = form.cleaned_data['vidBlob']
name = form.cleaned_data['name']
description = form.cleaned_data['descrip']
#logic to turn video blob into video file and then upload to 3rd party storage service
return super().form_valid(form)
答:
我掌握了工作方法。我的问题是,在我尝试创建 blob 之前,MediaRecorder 还没有完成它的工作。这是我的完整脚本,所以你可以看到我是如何修复它的(创建和使stopCapture成为函数是关键(这样我就可以在尝试创建blob之前进行recordingPromise)):recordingPromise
async
await
JavaScript的
/* -------------------
-------- Capture -------
------------------- */
const vidDisplay = document.querySelector("#vidDisplay");
const startRecordBtn = document.querySelector("#startRecordBtn");
const stopRecordBtn = document.querySelector("#stopRecordBtn");
const sendBtn = document.querySelector("#sendBtn");
const blobInput = document.querySelector("#id_vidBlob");
const resultDisplay = document.querySelector("#result");
/* -------------------------
--------- Variables ----------
--------------------------- */
// gotta have the chunks
const recordedChunks = [];
// User media constraints
const constraints = {
audio: true,
video: {
width: 640,
height: 360
}
};
// declare stream globally
let stream;
// declare mediaRecorder globally
let mediaRecorder;
// declare recordingPromise globally
let recordingPromise;
// Recorder options
const recorderOptions = {
mimeType: "video/webm; codecs=vp9",
audioBitsPerSecond: 8000,
videoBitsPerSecond: 156250,
};
/* -------------------------
--------- Functions ----------
--------------------------- */
// Function for starting screen capture
const startCapture = async function() {
try {
stream = await navigator.mediaDevices.getUserMedia(constraints);
vidDisplay.srcObject = stream;
// create media recorder
mediaRecorder = new MediaRecorder(stream, recorderOptions);
mediaRecorder.ondataavailable = handleDataAvailable;
// start up recorder
mediaRecorder.start();
// Create a promise to resolve when the recording is stopped
recordingPromise = new Promise((resolve) => {
mediaRecorder.onstop = resolve;
});
} catch (err) {
console.error(err);
}
}
// Function for recorder
const handleDataAvailable = function(event) {
console.log("data is available");
if (event.data.size > 0) {
recordedChunks.push(event.data);
} else {
// …
}
}
// Function for stopping screen capture
const stopCapture = async function() {
let tracks = vidDisplay.srcObject.getTracks();
tracks.forEach((track) => track.stop());
vidDisplay.srcObject = null;
// stop ye recorder
mediaRecorder.stop();
await recordingPromise;
const blob = new Blob(recordedChunks, {type: "video/webm",}); // create blob from recordedChunks
// Convert the Blob to base64 and then JSON format to submit with form
const reader = new FileReader();
reader.onloadend = function () {
try {
const base64data = reader.result.split(',')[1];;
console.log(base64data);
// Create a JSON-formatted string with the base64-encoded blob data
const jsonData = JSON.stringify({ videoBlob: base64data });
// Set the value of the hidden input to the base64-encoded blob
blobInput.value = jsonData;
} catch (error) {
console.error('Error during FileReader operation:', error);
}
};
// read video data
reader.readAsDataURL(blob);
}
/* -------------------------
--------- Event Listeners ----------
--------------------------- */
startRecordBtn.addEventListener("click", startCapture);
stopRecordBtn.addEventListener("click", stopCapture);
这样一来,我就可以轻松地在表单中创建的 JSONField 中提交 blob,并且事实证明,在视图中使用它非常容易,这样我就可以将视频文件上传到第三方存储服务。
上一个:Django 中角色的电影演员表
评论