簡體   English   中英

是否可以從瀏覽器上傳亞馬遜s3上的流?

[英]Is it possible to upload stream on amazon s3 from browser?

我想捕獲網絡攝像頭視頻流,並直接將其流式傳輸到S3存儲。

我了解到你可以通過流上傳到s3: https//aws.amazon.com/blogs/aws/amazon-s3-multipart-upload/

我了解到你可以通過瀏覽器上傳: http//docs.aws.amazon.com/AmazonS3/latest/dev/HTTPPOSTExamples.html#HTTPPOSTExamplesFileUpload

但我仍然失去了如何實際做到這一點。

我需要一個上傳getinmedia流到S3的例子。

緩沖區,二進制數據,分段上傳,流...這是我所不知道的。 我希望我知道的東西,但現在甚至不在哪里學習。

目前,您不能簡單地將媒體流傳遞給任何S3方法以自動執行分段上傳。

但是,仍有一個名為dataavailable的事件,它在每個給定的時間間隔內產生視頻塊。 因此,我們可以訂閱dataavailable並手動執行S3 Multipart Upload。

這種方法帶來了一些復雜性:說每1秒生成一大塊視頻,但我們不知道將塊上傳到S3需要多長時間。 例如,由於連接速度,上傳可能需要3倍的時間。 因此,我們可能會在嘗試同時發出多個PUT請求時陷入困境。

可能的解決方案是逐個上傳塊,並且不要開始上傳下一個塊直到prev。 一個上傳。 以下是使用Rx.js和AWS SDK如何處理此問題的片段。 請看我的評論。

 // Configure the AWS. In this case for the simplicity I'm using access key and secret. AWS.config.update({ credentials: { accessKeyId: "YOUR_ACCESS_KEY", secretAccessKey: "YOUR_SECRET_KEY", region: "us-east-1" } }); const s3 = new AWS.S3(); const BUCKET_NAME = "video-uploads-123"; let videoStream; // We want to see what camera is recording so attach the stream to video element. navigator.mediaDevices .getUserMedia({ audio: true, video: { width: 1280, height: 720 } }) .then(stream => { console.log("Successfully received user media."); const $mirrorVideo = document.querySelector("video#mirror"); $mirrorVideo.srcObject = stream; // Saving the stream to create the MediaRecorder later. videoStream = stream; }) .catch(error => console.error("navigator.getUserMedia error: ", error)); let mediaRecorder; const $startButton = document.querySelector("button#start"); $startButton.onclick = () => { // Getting the MediaRecorder instance. // I took the snippet from here: https://github.com/webrtc/samples/blob/gh-pages/src/content/getusermedia/record/js/main.js let options = { mimeType: "video/webm;codecs=vp9" }; if (!MediaRecorder.isTypeSupported(options.mimeType)) { console.log(options.mimeType + " is not Supported"); options = { mimeType: "video/webm;codecs=vp8" }; if (!MediaRecorder.isTypeSupported(options.mimeType)) { console.log(options.mimeType + " is not Supported"); options = { mimeType: "video/webm" }; if (!MediaRecorder.isTypeSupported(options.mimeType)) { console.log(options.mimeType + " is not Supported"); options = { mimeType: "" }; } } } try { mediaRecorder = new MediaRecorder(videoStream, options); } catch (e) { console.error("Exception while creating MediaRecorder: " + e); return; } //Generate the file name to upload. For the simplicity we're going to use the current date. const s3Key = `video-file-${new Date().toISOString()}.webm`; const params = { Bucket: BUCKET_NAME, Key: s3Key }; let uploadId; // We are going to handle everything as a chain of Observable operators. Rx.Observable // First create the multipart upload and wait until it's created. .fromPromise(s3.createMultipartUpload(params).promise()) .switchMap(data => { // Save the uploadId as we'll need it to complete the multipart upload. uploadId = data.UploadId; mediaRecorder.start(15000); // Then track all 'dataavailable' events. Each event brings a blob (binary data) with a part of video. return Rx.Observable.fromEvent(mediaRecorder, "dataavailable"); }) // Track the dataavailable event until the 'stop' event is fired. // MediaRecorder emits the "stop" when it was stopped AND have emitted all "dataavailable" events. // So we are not losing data. See the docs here: https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/stop .takeUntil(Rx.Observable.fromEvent(mediaRecorder, "stop")) .map((event, index) => { // Show how much binary data we have recorded. const $bytesRecorded = document.querySelector("span#bytesRecorded"); $bytesRecorded.textContent = parseInt($bytesRecorded.textContent) + event.data.size; // Use frameworks in prod. This is just an example. // Take the blob and it's number and pass down. return { blob: event.data, partNumber: index + 1 }; }) // This operator means the following: when you receive a blob - start uploading it. // Don't accept any other uploads until you finish uploading: http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-concatMap .concatMap(({ blob, partNumber }) => { return ( s3 .uploadPart({ Body: blob, Bucket: BUCKET_NAME, Key: s3Key, PartNumber: partNumber, UploadId: uploadId, ContentLength: blob.size }) .promise() // Save the ETag as we'll need it to complete the multipart upload .then(({ ETag }) => { // How how much bytes we have uploaded. const $bytesUploaded = document.querySelector("span#bytesUploaded"); $bytesUploaded.textContent = parseInt($bytesUploaded.textContent) + blob.size; return { ETag, PartNumber: partNumber }; }) ); }) // Wait until all uploads are completed, then convert the results into an array. .toArray() // Call the complete multipart upload and pass the part numbers and ETags to it. .switchMap(parts => { return s3 .completeMultipartUpload({ Bucket: BUCKET_NAME, Key: s3Key, UploadId: uploadId, MultipartUpload: { Parts: parts } }) .promise(); }) .subscribe( ({ Location }) => { // completeMultipartUpload returns the location, so show it. const $location = document.querySelector("span#location"); $location.textContent = Location; console.log("Uploaded successfully."); }, err => { console.error(err); if (uploadId) { // Aborting the Multipart Upload in case of any failure. // Not to get charged because of keeping it pending. s3 .abortMultipartUpload({ Bucket: BUCKET_NAME, UploadId: uploadId, Key: s3Key }) .promise() .then(() => console.log("Multipart upload aborted")) .catch(e => console.error(e)); } } ); }; const $stopButton = document.querySelector("button#stop"); $stopButton.onclick = () => { // After we call .stop() MediaRecorder is going to emit all the data it has via 'dataavailable'. // And then finish our stream by emitting 'stop' event. mediaRecorder.stop(); }; 
 button { margin: 0 3px 10px 0; padding-left: 2px; padding-right: 2px; width: 99px; } button:last-of-type { margin: 0; } p.borderBelow { margin: 0 0 20px 0; padding: 0 0 20px 0; } video { height: 232px; margin: 0 12px 20px 0; vertical-align: top; width: calc(20em - 10px); } video:last-of-type { margin: 0 0 20px 0; } 
 <div id="container"> <video id="mirror" autoplay muted></video> <div> <button id="start">Start Streaming</button> <button id="stop">Stop Streaming</button> </div> <div> <span>Recorded: <span id="bytesRecorded">0</span> bytes</span>; <span>Uploaded: <span id="bytesUploaded">0</span> bytes</span> </div> <div> <span id="location"></span> </div> </div> <!-- include adapter for srcObject shim --> <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/aws-sdk/2.175.0/aws-sdk.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"></script> 

注意事項:

  • 所有分段上傳都需要完成或中止。 如果您將其永久保留,將向您收取費用。 請參閱此處的“注釋”。
  • 您上傳的每個塊(最后一個除外)必須大於5 MB。 否則會拋出錯誤。 這里查看詳細信息。 所以你需要調整時間范圍/分辨率。
  • 在實例化SDK時,請確保存在具有s3:PutObject權限的策略。
  • 您需要在您的存儲桶CORS配置中公開ETag。 以下是CORS配置的示例:

 <?xml version="1.0" encoding="UTF-8"?> <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <CORSRule> <AllowedOrigin>*</AllowedOrigin> <AllowedMethod>GET</AllowedMethod> <AllowedMethod>POST</AllowedMethod> <AllowedMethod>PUT</AllowedMethod> <ExposeHeader>ETag</ExposeHeader> <AllowedHeader>*</AllowedHeader> </CORSRule> </CORSConfiguration> 

限制:

  • 請注意,因為MediaRecorder API仍未被廣泛采用。 確保您在使用它之前檢查您是否訪問過caniuse.com

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM