A New Collection of Thoughtful Learning Apps — Now Available on iOS & Android

Image
I’m excited to share a set of mobile apps I’ve recently completed and published on both the Google Play Store and the Apple App Store. These apps are designed with a simple goal in mind: to make meaningful, structured content more accessible, whether you’re studying theology or improving your English vocabulary. 📱 Now Available on Both Platforms All apps are live and available for download: Google Play Developer Page: https://play.google.com/store/apps/dev?id=5835943159853189043 Apple App Store Developer Page: https://apps.apple.com/ca/developer/q-z-l-corp/id1888794100 📖 Theology & Confession Study Apps For those interested in Reformed theology and classical Christian teachings, I’ve developed a series of apps that present foundational texts in a clean, focused reading format: The Belgic Confession Canons of Dort Heidelberg Catechism Westminster Shorter Catechism Each app is designed to provide a distraction-free experience, making it easier to read, reflect, and revisit these im...

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

❤️ Support This Blog


If this post helped you, you can support my writing with a small donation. Thank you for reading.


Comments

Popular Posts

Fix “A problem occurred starting process 'command node'” in Android Studio for React Native

Fix up watchman issue with Ghost

Using Mutual TLS (mTLS) in Next.js (Server-Side Only)