background
Banner

Bambi Twins

Bambi-Editor comes in two build flavors: Desktop and Web Start. If you're a regular user that wants to have fun with images, save them to a computer and maybe print them, you want to download and install the desktop edition. If you're a website owner or developer that wants to give end end users ability to upload images specifically cropped and/or scaled to your requirements, you want Java Web Start edition. Upon each release, Bambi dev team distributes both editions simultaneously. They're both released to Sourceforge. Desktop is available for a traditional download, Web Start is available as a live JAR that can be linked to via JNLP. Feature-wise they are identical, but they are configured differently.
				# codebase /bambi-editor
				# compiles and builds desktop distribution
				mvn clean package -P app
Web Start build (below) packs dependencies differently, and excludes configuration because it needs to be externalized. You never have to execute this build because you can link to our latest JAR instead.
				# codebase /bambi-editor
				# compiles and builds web start distribution
				mvn clean package -P ws
In addition, Web Start build provides for custom splash screen and external configuration. You want to either execute this build with your own webstart configuration, or somehow (Ant, manually, etc) generate config JAR similarly to how this build does it.
				# codebase /bambi-webstart
				# compiles webstart JAR and config JAR
				mvn clean package -Djnlp.host=bambieditor.sourceforge.net -Dupload.dir=

So You're a Web Developer?

To run Bambi-Editor as JWS on your website you need to provide a JNLP (Java Network Launching Protocol) script. In that script you will reference your own configuration JAR, which must include configuration file and (optionally) any custom plugins you may have written.
<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="http://bambieditor.sourceforge.net/bambi/" href="bambi.jnlp">

    <information>
        <title>Bambi Editor</title>
        <vendor>mrazjava</vendor>
    </information>

    <resources>
    	<property name="jnlp.packEnabled" value="false"/>
    	<!-- <property name="jnlp.bambieditor.splash-title" value="<html><h1>Custom Title</h1></html>" /> -->
        <!-- Application Resources -->
        <j2se version="1.7+" href="http://java.sun.com/products/autodl/j2se" initial-heap-size="128m" max-heap-size="1024m" />
        <jar href="bambi-config-0.9.1.jar" download="lazy" />
        <extension name="core" href="http://bambieditor.sourceforge.net/bambi/bambi-core.jnlp"/>
    </resources>

    <application-desc  
    	main-class="org.zimowski.bambi.editor.BambiApp" 
    	progress-class="org.zimowski.bambi.webstart.WebStartProgress" />

	<update check="always" policy="always"/>
</jnlp>
Line 14 references your config JAR, which must be signed. Line 15 points to our latest compiled and signed Bambi-Editor JAR. Since line 15 links to a prepared JAR, you can host Bambi with very little effort! Neat, isn't it?

An example of custom configured BambiEditor running of a different domain can be seen on my personal server. There you can launch bambi with custom configuration but core (extension) still pointing to our sourceforge base.

Of course, if you want you can download source code and build entire thing yourself without linking to our JAR. We assume, that if you're that adventurous you know what you're doing.

Singing the Bambi Song

When user uploads a photo from Bambi-Editor, server processing script needs to sing to the same tune that Bambi does. It is not required to use any particular technology, it just needs to comply with Bambi's upload plugin protocol. Bambi-Editor ships with two upload plugins out of the box, which should cover most needs: They both get end-user's image to your server, though the form post plugin is by far the more flexible of the two. FTP plugin does not require a server side script, form post does. It is the form post plugin ability to communicate with a script that makes it so powerful and versatile. In this article we focus on explaining how to accept Bambi image upload via form post with PHP server script saving a photo to MySQL blob.

In general, the form-post data flow scheme is as following:

On this demo website image upload is saved by PHP script into MySQL blob.
FTP upload scheme is similar, except there is no script, so image is always saved to server's filesystem.

So the scenario is this: Your end users play with their image applying various Bambi filters, then when ready upload it over internet targetting a URL you configured. Bambi handles all the latency issues so your user sees a real-time progress on the UI. Your processing script (URL) is essentially free to do anything it wants with the image. Typically a script will save the image, either to a local filesystem or a database blob, but there is nothing precluding it from doing other things like sending it via e-mail or posting to a twitter account. Possibilities are endless. Here is a PHP script this demo site uses to process image upload via form post:

<?php
include "include/db.php"; // your db configuration

//
// Form post upload processor that requires explicit authentication.
//
// Response Protocol:
// KEY|VALUE - one line, lines can be in any order
// Keys:
// STATUS - OK or ERROR, required
// RECEIVED - number of bytes received, required
// PROCESSED - number of bytes actually processed, required
// DATE - server date/time when request was processed, optional
// MSG - message from server (error or info), optional

$uid = 0;
$email=$_SERVER['HTTP_USER_ID'];
$pass=$_SERVER['HTTP_USER_PASS'];
$expected_bytes = (int) $_SERVER['HTTP_STREAM_SIZE'];
$f = $_FILES['filename']['tmp_name'];
$fsize = filesize($f);
$p = $_GET['p'];

if($email != null && $pass != null) {
	$sql = "select id from t_user where email = '{$email}' and password = '{$pass}'";
	if(!$result = $db->query($sql)) die($db->error);
	if($result->num_rows == 1) {
		$row = $result->fetch_assoc();
		$result->free();
		$uid = $row['id'];
	}
	else {
		echo "RECEIVED|$fsize\r\n";
		echo "PROCESSED|0\r\n";
		echo "STATUS|ERROR\r\n";
		echo "MSG|Invalid Login\r\n";
		echo "DATE|".date("Y-m-d H:i:s");
		return;
	}
	
	$data = addslashes(fread(fopen($f, "r"), $fsize));

	if($expected_bytes == $fsize) {
		$sql = "select count(*) as c from t_pic where user_id = $uid";
		if(!$result = $db->query($sql)) die($db->error);
		$row = $result->fetch_assoc();
		$result->free();
		if($row['c'] > 0) {
			$sql = "update t_pic set pic{$p} = '{$data}' where user_id = {$uid}";
		}
		else {
			$sql = "insert into t_pic (user_id, pic{$p}) values ({$uid},'{$data}')";
		}

		$db->query($sql);
		
		echo "RECEIVED|$fsize\r\n";
		echo "PROCESSED|$fsize\r\n";
		echo "DATE|".date("Y-m-d H:i:s")."\r\n";
		echo "MSG|{$p}\r\n";
		echo "STATUS|OK";
	}
	else {
		echo "RECEIVED|$fsize\r\n";
		echo "PROCESSED|0\r\n";
		echo "STATUS|ERROR\r\n";
		echo "MSG|byte mismatch: [expected $expected_bytes, received: $fsize]\r\n";
		echo "DATE|".date("Y-m-d H:i:s");
	}
}
else {
	echo "RECEIVED|$fsize\r\n";
	echo "PROCESSED|0\r\n";
	echo "STATUS|ERROR\r\n";
	echo "MSG|Incomplete Login Info\r\n";
	echo "DATE|".date("Y-m-d H:i:s");
}
$db->close();
?>
The script above verifies authentication credentials entered by the user into Bambi-Editor, and saves the image to a database blob if user authenticated correctly. Notice the script above will reject an image if user authentication fails. In our demo implementation user password is stored securely as MD5 digest:
Passwords stored in our live demo database are unreadable, secured by MD5.
But wait... In the PHP script we don't see md5('') function invoked in the SQL on the {$pass} value that came from $_GET. That's because Bambi-Editor is capable of encrypting values before they're sent over a wire! By the way, the encryption algorithm is a plugin and Bambi supports more than just MD5. So by the time password reaches the script it's already been hashed and we can simply compare both strings.

Here is a database used by the upload script above:

-- MySQL dump 10.13  Distrib 5.5.31, for debian-linux-gnu (x86_64)
--
-- Host: localhost    Database: bambi
-- ------------------------------------------------------
-- Server version	5.5.31-0ubuntu0.13.04.1

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `t_pic`
--

DROP TABLE IF EXISTS `t_pic`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `t_pic` (
  `user_id` int(11) NOT NULL,
  `pic1` mediumblob,
  `pic2` mediumblob,
  `pic3` mediumblob,
  `pic4` mediumblob,
  `pic5` longblob,
  PRIMARY KEY (`user_id`),
  KEY `fk_user_id` (`user_id`),
  CONSTRAINT `fk_user_id` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Table structure for table `t_user`
--

DROP TABLE IF EXISTS `t_user`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `t_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `email` varchar(45) NOT NULL,
  `password` char(32) NOT NULL,
  `date_created` datetime DEFAULT NULL,
  `fname` varchar(20) DEFAULT NULL,
  `lname` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `email_UNIQUE` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

-- Dump completed on 2013-06-03 15:00:12
We designed upload capabilities for five images per user account, but implemented the actual demo with first four disregarding pic5 longblob.

Closing Remarks

Enabling Bambi-Editor on your website is easy. All you need are the following components: While we plan to enhance Bambi-Editor with additional upload strategies (Socket Uploader for example) - you could write your own and contribute to us. We feel that plugins such as FacebookUploader, TwitterUploader and other social networking in nature will make Bambi even more useful!