如何使用JavaScript通过Django表单将视频blob发送到Django视图?

gc0ot86w  于 2023-11-20  发布在  Go
关注(0)|答案(1)|浏览(137)

In a nutshell

如何使用JavaScript通过Django表单将视频数据blob获取到Django视图?

背景

我正在构建一个录制网络摄像头视频的功能。我已经成功地解决了这一部分。我可以使用JavaScript来流式传输用户的摄像头和麦克风,记录流,将其转换为blob,然后我能够将blob转换为文件。然而,我不想要文件,因为我需要用户的视频简单地发送到我的后端,我不希望用户与视频文件进行交互(我刚刚了解到,由于安全原因,您不能以编程方式将<input type='file'>元素分配给文件)。* 这就是为什么我希望只发送二进制数据。*

What I've Tried(and what the result was)(我尝试了什么,结果是什么)

  • 就像我说的,我尝试在Django表单中使用FileField(但由于固有的Web安全限制,没有工作)。让用户自己下载和上传文件不符合项目要求。
  • 我试过在Django表单中使用JSONField并提交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中的一个单独视图(使用formData()fetch())。然而,我宁愿不这样做,因为我喜欢Django表单提供的内置安全性。
  • 也许最好先把数据发送到我的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)

qni6mghb

qni6mghb1#

我得到了工作的方法。我的问题是MediaRecorder在我尝试创建blob之前没有完成它的工作。这里是我的完整脚本,所以你可以看到我是如何修复它的(创建recordingPromise并使stopCapture成为async函数是关键(这样我就可以在尝试创建blob之前await recordingPromise)):

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,并且证明在视图中使用它非常容易,这样我就可以将视频文件上传到第三方存储服务。

相关问题