Upload to s3 directly from Flash 403 issue - actionscript-3

there are quite a few ways to upload to S3 from flash. I am trying to implement code from the following example on Amazon's site http://aws.amazon.com/code/1092?_encoding=UTF8&jiveRedirect=1
Some posts that I ran across indicated that a lot of the fields from Amazon are now Requiered and unless you fill them in you will get this dreaded 403 error.
I have tried several things and I am hoping there will be a solution soon. I used the following libs from here http://code.google.com/p/as3awss3lib/.
Here is my class that handles all the uploading
package com.myemma.s3uploader.main.controllers
{
import jp.classmethod.aws.core.AWSEvent;
import s3.flash.S3PostOptions;
import s3.flash.S3PostRequest;
import utils.PolicyGenerator;
import com.myemma.s3uploader.main.model.MainDM;
import com.myemma.s3uploader.settings.Settings;
import flash.events.DataEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.external.ExternalInterface;
import flash.net.FileReference;
/**
* #author Matthew Sloan Wallace - http://mattwallace.me
*/
public class UploadFilesAction extends EventDispatcher
{
[Inject]
public var dm : MainDM;
private var service : S3PostRequest;
[Init]
public function onInit() : void
{
if (ExternalInterface.available)
ExternalInterface.addCallback( "uploadFiles", uploadFiles );
}
private function uploadFiles() : void
{
if (dm.selectedFiles)
upload();
}
private function upload() : void
{
if (dm.selectedFiles.length > 0)
{
var fileReference : FileReference = dm.selectedFiles[0];
// var s3:AWSS3 = new AWSS3(Settings.accessKey, Settings.secret_key);
// s3.saveObject("mattwallace", fileReference.name, "image/png", fileReference);
// s3.addEventListener("objectSaved", onObjectSaved);
var policy : PolicyGenerator = PolicyGenerator.getInstance( Settings.accessKey, Settings.secret_key );
var s3Options : S3PostOptions = new S3PostOptions();
s3Options.secure = false;
s3Options.acl = "private";
s3Options.contentType = "image/png";
s3Options.filename = fileReference.name;
s3Options.success_action_status = "201";
s3Options.policy = policy.policy;
s3Options.signature = policy.signature;
service = new S3PostRequest( Settings.accessKey, Settings.bucket, Settings.secret_key, s3Options );
service.addEventListener( Event.OPEN, function( event : Event ) : void
{
trace( "Uploading..." );
trace( "Upload started: " + fileReference.name );
} );
service.addEventListener( ProgressEvent.PROGRESS, function( event : ProgressEvent ) : void
{
trace( Math.floor( event.bytesLoaded / event.bytesTotal * 100 ) );
} );
service.addEventListener( IOErrorEvent.IO_ERROR, function( event : IOErrorEvent ) : void
{
trace( "Upload error!" );
trace( "An IO error occurred: " + event );
} );
service.addEventListener( SecurityErrorEvent.SECURITY_ERROR, function( event : SecurityErrorEvent ) : void
{
trace( "Upload error!" );
trace( "A security error occurred: " + event );
} );
service.addEventListener( DataEvent.UPLOAD_COMPLETE_DATA, function( event : Event ) : void
{
trace( "Upload complete!" );
trace( "Upload completed: " + event );
dm.selectedFiles.splice( 0, 1 );
} );
try
{
service.upload( fileReference );
}
catch(e : Error)
{
trace( "Upload error!" );
trace( "An error occurred: " + e );
}
}
}
}
}

I'd worked with s3 and I didn't used special libs for that (maybe except generating policy and signature). Try something like the following:
var policyURL = "..."; //something like "http://domain.s3.amazonaws.com/crossdomain.xml"
flash.security.Security.loadPolicyFile(policyURL);
fileReference.addEventListener(Event.SELECT, selectHandler);
fileReference.browse();
function selectHandler(e: Event) {
fileReference = event.target;
s3options = new flash.net.URLVariables();
s3Options.secure = false;
s3Options.acl = "private";
s3Options.contentType = "image/png";
s3Options.filename = fileReference.name;
s3Options.success_action_status = "201";
s3Options.policy = policy;
s3Options.signature = signature;
uploadURL = new flash.net.URLRequest();
uploadURL.data = s3Options;
fileReference.upload(uploadURL, "file", false);
}

I managed to get my code to work, the difficulty is to get the right policy. I'm using elctech's library to generate the s3 upload call.
import com.elctech.*;
public class Uploader{
private var _options : S3UploadOptions = new S3UploadOptions();
// ...
public function uploadPDF(tempFile:File):void{
_options.FileSize = tempFile.size.toString();
_options.FileName = getFileName(tempFile);
_options.ContentType = 'application/pdf';
_options.key = certificate['key'] + _options.FileName;
_options.policy = certificate['policy'];
_options.signature = certificate['signature'];
_options.bucket = certificate['bucket'];
_options.AWSAccessKeyId = certificate['accessKey'];
_options.acl = certificate['acl'];
_options.Expires = certificate['expiration'];
_options.Secure = 'false';
_options.successactionstatus = certificate['sas'];
var request:S3UploadRequest = new S3UploadRequest(_options);
configureUploadListeners(request);
try {
request.upload(tempFile);
} catch(e:Error) {
trace("An error occurred: " + e);
}
}
private function getFileName(file:FileReference):String {
var fileName:String = file.name.replace(/^.*(\\|\/)/gi, '').replace(/[^A-Za-z0-9\.\-]/gi, '_');
return fileName;
}
}
I get the amazon s3 certificate from our main Rails application via our API, here's what the policy generation looks like:
def certificate
bucket = ENV['S3_BUCKET']
access_key = ENV['S3_KEY']
secret = ENV['S3_SECRET']
key = "temp/"
expiration = 10.hours.from_now.utc.strftime('%Y-%m-%dT%H:%M:%S.000Z')
max_filesize = 500.megabytes
acl = 'public-read'
sas = '201'
policy = Base64.encode64(
"{'expiration': '#{expiration}',
'conditions': [
{'bucket': '#{bucket}'},
['starts-with', '$key', '#{key}'],
{'acl': '#{acl}'},
{'Content-Type': 'application/pdf'},
{'Content-Disposition': 'attachment'},
['starts-with', '$Filename', ''],
['eq', '$success_action_status', '201']
]}
").gsub(/\n|\r/, '')
signature = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha1'), secret, policy)).gsub(/\n| |\r/, '')
{ :access_key => access_key, :key => key, :policy => policy, :signature => signature, :sas => sas, :bucket => bucket, :acl => acl, :expiration => expiration }
end

Related

class circular dependency resolve with inversifyJS

I have tried to understand how to resolve circular dependencies using InversifyJS but I'm still having an issue.
I have a class Leader which needs to have a Follower and the Follower instance must be bound to Leader. I'm using lazyInject from inversifyJS but still when report() function of follower is executed it sees leader property as undefined.
Here's follows an example that demonstrates my problem:
import { Container, inject, injectable } from "inversify";
import "reflect-metadata";
import getDecorators from "inversify-inject-decorators";
const SYMBOLS = {
LEADER: Symbol("LEADER"),
FOLLOWER: Symbol("FOLLOWER"),
};
const container = new Container({
autoBindInjectable: true,
defaultScope: "Singleton",
});
const { lazyInject } = getDecorators(container);
#injectable()
class LeaderClass {
#inject(SYMBOLS.FOLLOWER) follower1!: FollowerClass;
public sn = "Ldr-" + Math.floor(Math.random() * 1000);
public report = () => {
console.log("Leader:", this.sn);
console.log("Follower1: ", this.follower1.sn);
};
}
#injectable()
class FollowerClass {
#lazyInject(SYMBOLS.LEADER) leader!: LeaderClass;
public sn = "Flw-" + Math.floor(Math.random() * 1000);
public report = () => {
console.log("Follower:", this.sn);
console.log("Leader:", this.leader.sn);
};
}
container.bind<LeaderClass>(SYMBOLS.LEADER).to(LeaderClass);
container.bind<FollowerClass>(SYMBOLS.FOLLOWER).to(FollowerClass);
const leader = container.get<LeaderClass>(SYMBOLS.LEADER);
const follower = container.get<FollowerClass>(SYMBOLS.FOLLOWER);
console.log("--------");
console.log(" ");
console.log(leader.report());
console.log(follower.report());
The line:
console.log(follower.report());
throws an error because follower.leader is undefined.
Any suggestions on how to solve such situations?

How to fix DOMException error in google chrome?

I work with sounds in a browser game. I wrote sound manager. everything works fine, but not in Google chrome. I handled the error "uncaught (in promise) domexception", after the sounds were played in 50 percent of cases, in other cases it returns the error DOMException. What could be the problem?
export class AudioFile{
private audio: HTMLAudioElement;
private fileMP3: string;
private fileOGG: string;
private volume = 1;
private loop = false;
constructor(MP3:string, OGG:string) {
this.audio = new Audio();
this.fileMP3 = MP3;
this.fileOGG = OGG;
this.audio.canPlayType('audio/mpeg') ? this.audio.src = this.fileMP3 : this.audio.src = this.fileOGG;
this.audio.load();
this.audio.volume = this.volume;
this.audio.loop = this.loop;
}
public play() {
this.audio.currentTime = 0;
const playPromise = this.audio.play();
if (playPromise !== undefined) {
playPromise.then(_ => {
})
.catch(error => {
console.log(error);
});
}
}
public stop() {
this.audio.pause();
}
}
``````````````sound manager`````````````
export class SoundManager {
private sounds = new Map();
private static _soundManager: SoundManager;
constructor(){
if (SoundManager._soundManager) {
throw new Error("Instantiation failed: "+
"use Singleton.getInstance() instead of new.");
}
SoundManager._soundManager = this;
}
public static get instance(): SoundManager {
if (this._soundManager)
return this._soundManager;
else
return this._soundManager = new SoundManager();
}
preload() {
const pathMP3 = SoundConfig.PATHMP3;
const pathOGG = SoundConfig.PATHOGG;
for (const item in SoundConfig.SOUNDS) {
const name = SoundConfig.SOUNDS[item].NAME;
this.sounds.set(name, new AudioFile(pathMP3 + name + '.mp3', pathOGG + name + '.ogg'));
}
}
getSound(id: string): AudioFile {
return this.sounds.get(id);
}
}
Thank you spendr.
error: DOMException
code: 0
message: "play() failed because the user didn't interact with the document first.
Game runs through the iframe and I was needed to add a feature policy for autoplay.
<iframe src="..." allow="autoplay">
The article that helped me in solving the problem

Setting data in viewModel knockoutjs from html5 websocket

I am trying to create knockout.js component that is getting data from HTML5 Websocket. Websocket code is in separate script e.g. util.js. I am able to connect and get data from socket, but dont know how correctly to set corresponding property in component`s ViewModel.
Websocket - util.js:
var options = {
server: '127.0.0.1',
port: '12345'
};
var socket, loadedFlag;
var timeout = 2000;
var clearTimer = -1;
var data = {};
function handleErrors(sError, sURL, iLine)
{
return true;
};
function getSocketState()
{
return (socket != null) ? socket.readyState : 0;
}
function onMessage(e)
{
data=$.parseJSON(e.data);
// ???? Is it possible to have here something like
// ???? viewModel.getDataWS1(data);
}
function onError()
{
clearInterval(clearTimer);
socket.onclose = function () {
loadedFlag = false;
};
clearTimer = setInterval("connectWebSocket()", timeout);
}
function onClose()
{
loadedFlag = false;
clearInterval(clearTimer);
clearTimer = setInterval("connectWebSocket()", timeout);
}
function onOpen()
{
clearInterval(clearTimer);
console.log("open" + getSocketState());
}
function connectWebSocket()
{
if ("WebSocket" in window)
{
if (getSocketState() === 1)
{
socket.onopen = onOpen;
clearInterval(clearTimer);
console.log(getSocketState());
}
else
{
try
{
host = "ws://" + options.server + ":" + options.port;
socket = new WebSocket(host);
socket.onopen = onOpen;
socket.onmessage = function (e) {
onMessage(e);
};
socket.onerror = onError;
socket.onclose = onClose;
}
catch (exeption)
{
console.log(exeption);
}
}
}
}
Component (productDisplay.js) - creating so that is can be used on multiple pages:
define([
'jquery',
'app/models/productDisplayModel',
'knockout',
'mapping',
'socket'
],
function ($, model, ko, mapping) {
ko.components.register('product', {
viewModel: {require: 'app/models/productModel'},
template: {require: 'text!app/views/product.html'}
});
});
Product ViewModel (productModel.js) - where I struggle to set viewModel property to data from websocket:
var viewModel = {};
define(['knockout', 'mapping', 'jquery'], function (ko, mapping, $) {
function Product(name, rating) {
this.name = name;
this.userRating = ko.observable(rating || null);
}
function MyViewModel() {
this.products = ko.observableArray(); // Start empty
}
MyViewModel.prototype.getDataWS1 = function () {
//Websocket has not connected and returned data yet, so data object is empty
// ???? Is there anyway I can add something like promise so that the value is set once socket is connected?
this.products(data);
};
// apply binding on page load
$(document).ready(function () {
connectToServer1();
viewModel = new MyViewModel();
ko.applyBindings(viewModel);
viewModel.getDataWS1();
});
});
Thank you for any ideas.
You can update an observable when you get a message in the following manner:
util.js
function onMessage(e) {
var productData = $.parseJSON(e.data);
viewModel.addNewProduct(productData);
}
productModel.js
function Product(name, rating) {
this.name = name;
this.userRating = ko.observable(rating || null);
}
function MyViewModel() {
this.products = ko.observableArray(); // Start empty
}
MyViewModel.prototype.addNewProduct(product) {
var newProduct = new Product(product.name, product.rating);
this.products.push(newProduct);
}
Basically the idea is that when you get a message (in onMessage function), you will parse the data and call a function in your viewmodel to add the message data to the viewmodel properties (observables, observableArrays, etc.)

AS3 Error with POST but not with GET (#2032 Stream Error)

This should be simple. I'm trying to send POST data to a server and have written a custom package for it as follows:
package com.cron {
import flash.events.*
import flash.net.*
public class Mail {
var scriptURL = "http://www.[mydomain].com/scripts/flashmail.php";
var pass = '[mypassword]'
var productName = '[myProduct]'
var fromemail = 'noreply#[mydomain].com'
var scriptLoader,scriptRequest,scriptVars,onComplete
public function Mail(ProductName=null, Fromemail=null, OnComplete=null, ScriptURL=null, Pass=null) {
//SET PARAMS:
scriptURL = (ScriptURL ? ScriptURL : scriptURL)
productName = (ProductName ? ProductName : productName)
fromemail = (Fromemail ? Fromemail : fromemail)
pass = (Pass ? Pass : pass)
onComplete = OnComplete ? OnComplete : null
//SETUP CLASS:
scriptRequest = new URLRequest(scriptURL);
scriptLoader = new URLLoader();
scriptVars = new URLVariables();
}
public function sendmail(toemail,subject,msg){
 
scriptLoader.addEventListener(Event.COMPLETE, handleLoadSuccessful);
scriptLoader.addEventListener(IOErrorEvent.IO_ERROR, handleLoadError);
scriptLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, handleLoadError);
 
scriptVars.pass = pass;
scriptVars.productname = productName;
scriptVars.toemail = toemail;
scriptVars.subject = subject;
scriptVars.msg = msg;
scriptVars.fromemail = fromemail;
//trace(scriptVars)
scriptRequest.method = URLRequestMethod.POST;
scriptRequest.data = scriptVars;
G.traceObj(scriptRequest) 
scriptLoader.load(scriptRequest);
trace("MAIL: Sending mail to "+toemail)
}
private function handleLoadSuccessful($evt:Event):void{
if(String(scriptLoader.data) != '1'){
trace("MAIL: Error with script:")
trace(scriptLoader.data)
if(onComplete) onComplete(0);
return;
}else{
trace('MAIL: Sent successfully')
if(onComplete) onComplete(1);
}
}
 
private function handleLoadError($evt):void{
    trace("MAIL: Connection Failed with error: "+$evt);
if(onComplete) onComplete(0);
}
}
}
If I change URLRequestMethod.POST to URLRequestMethod.GET, this all works great. However, using POST, I get the following errors:
MAIL: Connection Failed with error: [HTTPStatusEvent type="httpStatus" bubbles=false cancelable=false eventPhase=2 status=0 responseURL=null]
MAIL: Connection Failed with error: [IOErrorEvent type="ioError" bubbles=false cancelable=false eventPhase=2 text="Error #2032: Stream Error. URL: http://www.[myDomain].com/scripts/flashmail.php"]
Bizzarely, if I upload this to my server, it works fine! It just refuses to work with a local swf or packaged AIR app. I need to deploy this on Android so it's crucial I get it working locally.
btn.addEventListener(MouseEvent.CLICK,handler);
function handler(event:MouseEvent):void
{
var url:URLLoader = new URLLoader();
url.dataFormat = URLLoaderDataFormat.VARIABLES;
var request:URLRequest = new URLRequest("http://www.[myDomain].com/scripts/flashmail.php");
var rv:URLVariables = new URLVariables();
rv.user_ = user_.text;
request.data = rv;
request.method = URLRequestMethod.POST;
url.load(request);
url.addEventListener(Event.COMPLETE,eventComplete);
url.addEventListener(IOErrorEvent.IO_ERROR,ioerror);
function eventComplete(e:Event):void
{
if (e.target.data.success == "ok")
{
trace('result:' + e.target.data.success2);
}
else
{
trace('no result');
}
}
}
function ioerror(e:Event):void
{
trace("Connection Error");
}

Serialize Object like a form in as3

I am looking to transmit a post request through HTTP, the body needs to contain information from a ValueObject I've already created. Essentially I need to serialize the object (which has multiple dimensions). The problem I'm running into is that I don't want to transmit the data as JSON or XML but as a multidimensional array. It works in HTML forms like:
data:
Array
(
[a] => Array
(
[a] => Array
(
[1] => abcd
)
[b] => defg
)
[d] => ghij
)
request body (decoded):
a[a][1]=abcd&a[b]=defg&d=ghij
Is there any library out there that does this kind of serialization already? I'm using as3httpclientlib, so I don't have the convenience of the mx.rpc family. I've spent the past several hours trying to roll my own solution, but I'm pretty sure I'd be better off with a solution someone has put more time and thought into.
You can try AMF to solve your problem (you have different library according to your programming language).
The main advantage is that you can do mapping object to object (using Value Object) and preserve the data type (like arrays).
I'm using the plugin amf with Symfony and here is an example of data returned :
SabreAMF_TypedObject Object
(
[amfClassName:private] => Product
[amfData:private] => Array
(
[code] => 1970A007
[designation] => 1970 PELL A007 BLANC
[description] => Vernis extra souple extra brillant
[sommeil] =>
[name] => 1970
[base] => A Stretch Enduit
[composition] => 43% PL 57% PU
[laize] => 137-140 cm
[weight] => 588 g/ml
[dechirure] => 10 daN/9 daN
[abrasion] => 5000
[xenotest] => 4/5
[non_feu] => Non
[Color] => SabreAMF_TypedObject Object
(
[amfClassName:private] => Color
[amfData:private] => Array
(
[id] => 124
[name] => BLANC
)
)
Some explanation :
[amfClassName:private] => Product
My Object is typed. As3 side i have a Product Value Object too wich contains array properties like "code", "designation", "description",... and Color (another typed Array with Value Object Color)
So you can easily have a multidimensional structure arrays...
Ok,
so here is something more simple using URLVariables :
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
import flash.net.URLVariables;
public class URLVariableExample extends Sprite {
private static const URL_SERVER:String = "http://localhost/testURL.php";
private var loader:URLLoader;
public function URLVariableExample() {
init();
}
private function init() : void {
var request:URLRequest = new URLRequest(URL_SERVER);
var urlVar:URLVariables = new URLVariables();
var a : Array = ["toto", "titi", "loulou"];
urlVar["urlTest[a][a]"] = a.toString();
urlVar["urlTest[a][b]"] = "desc";
urlVar["urlTest[d]"] = "defg";
request.data = urlVar;
request.method = URLRequestMethod.POST;
loader = new URLLoader();
loader.addEventListener(Event.COMPLETE, loaderComplete);
loader.load(request);
}
private function loaderComplete(event : Event) : void {
loader.removeEventListener(Event.COMPLETE, loaderComplete);
trace(URLLoader(event.currentTarget).data);
}
}
}
I requested a php file and just put a simple var_dump function.
Trace return :
array(2) {["d"]=>string(4) "defg"["a"]=> array(2) {["a"]=>string(16) "toto,titi,loulou["b"]=>string(4) "desc"}}
var url:String = 'http://localhost/dump.php';
var params:Object = {
test: 'ok',
nested_1: {
nested_2: {
nested_3: {
nested_4: {
hello: 'mf',
str: '~!##$%^&*()_+'
}
}
}
},
};
var request:URLRequest = new URLRequest(url);
var variables:URLVariables = new URLVariables();
parameters = fixParameters(parameters || {});
for (var key:String in parameters) {
variables[key] = parameters[key];
}
request.data = variables;
var loader:URLLoader = new URLLoader();
loader.load(request);
and here is fixParameters method
private function fixParameters(data:Object, parameters:Object = null, prefixes:Array = null):Object {
var setPrefix:Array;
var prefixKey:String;
if (!parameters) {
parameters = {};
}
if (!prefixes) {
prefixes = [];
}
for (var key:String in data) {
setPrefix = prefixes.concat([key]);
if (typeof(data[key]) == 'object') {
parameters = fixParameters(data[key], parameters, setPrefix);
} else {
prefixKey = '';
for (var i:Number = 0; i < setPrefix.length; i++) {
prefixKey += i == 0 ? setPrefix[i] : '[' + setPrefix[i] + ']';
}
parameters[prefixKey] = data[key];
}
}
return parameters;
}