AWS : how to play a video file from s3 bucket in browser

URI

all you have to do is to sign a request like:

https://s3.amazonaws.com/yourbucket/key?response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJWWH7EGSUBWE34IQ/20181228/us-east-1/s3/aws4_request&X-Amz-Date=20181228T074135Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=b3143f8442812e176fd61838813a33f53cfd7e198e1c6e68fab5d03d4a642403

and set it as src attribue to a video element.

Player component

import React, { Component } from 'react';
import PropTypes from 'prop-types';

const AWS_SERVICE = 's3';
const crypto = require('crypto');

Date.prototype.toYMDString = function() {
    let year = this.getFullYear().toString();
    let month = this.getUTCMonth() + 1;
    if (month < 10)
      month = "0" + month;
    let day = this.getUTCDate();
    if (day < 10)
      day = "0" + day;
    return String(year) + String(month) 
           + String(day);
}

Date.prototype.toTZString = function() {
    let hour = this.getUTCHours();
    if (hour < 10)
      hour = "0" + hour;
    let minute = this.getUTCMinutes();
    if (minute < 10)
      minute = "0" + minute;
    let second = this.getUTCSeconds();
    if (second < 10)
      second = "0" + second;
    return this.toYMDString() + "T"
      + String(hour) + String(minute)
      + String(second) + "Z";
}

class Player extends Component {
  constructor(props) {
    super(props);
  }

  Hmac(key, string) {
      const hmac = crypto.createHmac('sha256', key);
      hmac.end(string);
      return hmac.read();
  }
  
  Signature(date, region, service, toSign) {
    const { aws_secret_key } = this.props;
      let dateKey = this.Hmac('AWS4' + aws_secret_key, date);
      let dateRegionKey = this.Hmac(dateKey, region);
      let dateRegionServiceKey = this.Hmac(dateRegionKey, service);
      let signingKey = this.Hmac(dateRegionServiceKey, 'aws4_request');
      let signature = this.Hmac(signingKey, toSign).toString('hex');
      return signature;
  }
  
  stringToSign(timeStamp, scope, canonicalRequest) {
      var signParts = [];
      signParts.push('AWS4-HMAC-SHA256');
      signParts.push(timeStamp);
      signParts.push(scope);
      signParts.push(crypto.createHash('sha256').update(canonicalRequest).digest(
  'hex'));
      var result = signParts.join('\n');
      console.log('string to sign');
      console.log(result);
      return result;
  };
  
  canonicalRequest(method, uri, queryString, headers, signedHeaders, hashedPayload) {
    var canonicalParts = [];
    canonicalParts.push(method);
    canonicalParts.push(uri);
    canonicalParts.push(queryString);
    canonicalParts.push(headers);
    canonicalParts.push(signedHeaders);
    canonicalParts.push(hashedPayload);
    var result = canonicalParts.join('\n');
    console.log('canonical request');
    console.log(result);
    return result;
  }

  render() {
    const { aws_key, aws_region, bucket, key } = this.props;
    let uri = "/" + bucket + "/" + key;
    let time_stamp = new Date();
    let tz_time_stamp = time_stamp.toTZString();
    let ymd_time_stamp = time_stamp.toYMDString();
    let scope = ymd_time_stamp + "/" + aws_region + "/" + AWS_SERVICE + "/aws4_request";
    let queryString = "X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=" + encodeURIComponent(aws_key + "/" + scope) + "&X-Amz-Date=" + tz_time_stamp + "&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&response-content-disposition=inline";
    let headers = "host:s3.amazonaws.com";
    let signedHeaders = "";
    let hashedPayload = "host\nUNSIGNED-PAYLOAD";
    let request = this.canonicalRequest('GET', uri, queryString, headers, signedHeaders, hashedPayload);
    let signature = this.Signature(ymd_time_stamp, aws_region, AWS_SERVICE, this.stringToSign(tz_time_stamp, scope, request));
    let url = 'https://s3.amazonaws.com' + uri + '?response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=' + aws_key + "/" + scope + '&X-Amz-Date=' + tz_time_stamp + '&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=' + signature;
    console.log(url);
    return (
      <video src={url}
        autoPlay playsInline controls
        style={{width:320,height:240}}>
      </video>
    )
  }
}

Player.propTypes = {
  aws_key: PropTypes.string.isRequired,
  aws_secret_key: PropTypes.string.isRequired,
  aws_region: PropTypes.string.isRequired,
  bucket: PropTypes.string.isRequired,
  key: PropTypes.string.isRequired
};

export default Player;

Usage

    return (
        <Player aws_key={aws_key} bucket={bucket} aws_secret_key={aws_secret_key}
          key={key} aws_region={aws_region}>
        </Player>
    );

example

Subscribe to Post, Code and Quiet Time.

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe