• Examples (iOS)
• Examples (Android)
• Examples (C++)
Server API Documentation

 

Since 2.9.0

» Uploading files with SmartFoxServer 2X

In SmartFoxServer 2X 2.9.x we have added the ability to easily handle file uploads via regular HTTP Post requests. Since they are available on all supported platforms, the feature allow clients to rapidly upload any type of file from their desktop, laptop or mobile device. This in turn can be handled by custom server Extensions allowing developers to manage uploads of graphics, media, documents etc...

The upload request is handled via the internal Jetty HTTP Server which stores the received file in a temporary directory. From there a server side event, called FILE_UPLOAD is sent to the Zone Extension. Inside the Extension code the developer can move the file from the temp folder to the appropriate application directory, applying validation, processing the files, etc...

The diagram below illustrates the process we have just described.

upload

Each uploaded file is stored under {SFS2X}/data/uploads/ with a temporary file name to avoid collisions. From this position the files can then be moved in the application folders and organized according to the application requirements.

The FILE_UPLOAD server side event provides the following parameters:

Param name Param Type Description
USER User The sender of the uploaded file(s)
UPLOAD_FILE_LIST List<UploadedFile> List of uploaded files containing the original file name and the temp file name
UPLOAD_HTTP_PARAMS Map<String, String> A map of custom parameter names passed in the HTTP POST request

» Basic HTTP Request

Let's see how this work in practice with a basic HTML form. This in turn will help to understand how the POST request can be created programmatically from other languages such as Java, Objective-C, C#, Actionscript ...

It is important to note that SFS2X will only accept upload requests coming from logged in Users. A generic HTTP upload request, coming from an anonymous web page, will be refused.

In order for the following example to work correctly we assume that the following form is submitted after the client is already logged in the system, using the HTML 5 API.

<form action="http://localhost:8080/BlueBox/SFS2XFileUpload" method="post" enctype="multipart/form-data">
	<b>Select one or more files to upload:</b>
	<input type="file" multiple name="fileName"><br><br>
	<input type="hidden" name="sessHashId" value="01234567890abcdef" id="sessHashId">
	<input type="submit" value="Upload Files">
</form>

The mandatory parameters are:

The sessionId is obtained by reading the SmartFox.sessionToken after a successful login.

» Custom parameters

From the same upload form we can also send extra custom parameters that will be received by the Extension running in SmartFoxServer 2X. This option can be useful to provide extra data accompanying the uploaded files.

We can easily add any number of additional parameters by adding new hidden fields in the form and prefixing them with a double underscore ( __ ). All fields starting with this token will be passed in the UPLOAD_HTTP_PARAMS parameter of the server side FILE_UPLOAD event.

Here's the previous example form with additional custom fields:

<form action="http://localhost:8080/BlueBox/SFS2XFileUpload" method="post" enctype="multipart/form-data">
	<b>Select one or more files to upload:</b>
	<input type="file" multiple name="fileName"><br><br>
	<input type="hidden" name="sessHashId" value="01234567890abcdef" id="sessHashId">
    <input type="hidden" name="__color" value="Green" id="__color">
    <input type="hidden" name="__random" value="1255" id="__random">
    <input type="submit" value="Upload Files">
</form>

» Uploading from code

Let's now take a look at how the same HTML form can be transformed into an HTTP request from pure code.

Before we proceed we need to clarify that the upload request can be sent only after having logged in the server. Since all the details about how connecting and logging in are already described in the "Development Basics" section and in the examples provided we take for granted that you are already familiar with those two simple operations.

» Unity

In Unity it's best to stick with the classes provided by the engine itself instead of using .Net objects. Here's an example of how to upload using WWW and WWWForm.

IEnumerator UploadFileCo(string localFileName, string uploadURL)
{
		WWW localFile = new WWW("file:///" + localFileName);
		yield return localFile;
		
		if (localFile.error != null)
		{
				Debug.Log("Open file error: "+localFile.error);
				yield break; // stop the coroutine here
		}
		
		WWWForm postForm = new WWWForm();
		postForm.AddBinaryData("theFile", localFile.bytes, localFileName, "application/octet-stream");
		
		WWW upload = new WWW(uploadURL,postForm);        
		yield return upload;
		
		if (upload.error == null)
				Debug.Log("Upload done :" + upload.text);
		else
				Debug.Warn("Error during upload: " + upload.error);
}

void UploadFile(string localFileName, string uploadURL)
{
		StartCoroutine(UploadFileCo(localFileName, uploadURL));
}

// Upload file
UploadFile("/path/to/upload/file.zip", smartFox.httpUploadURI);

If you need extra parameters to be sent along with the upload you can add a properly formatted query string to the upload url like this:

string customParams="&__color=green&__random=42";

// Upload file
UploadFile("/path/to/upload/file.zip", smartFox.httpUploadURI + customParams);

» C# / .Net (not Unity)

Using .Net WebClient class we can easily upload any file to SFS2X.

public void UploadTest()  
{	
    // Create the WebClient object and send the file
    WebClient wc = new WebClient();
    wc.UploadFile(smartFox.httpUploadURI, "/path/to/upload/file.zip");
}

In the example we no longer need to build the upload URI ourselves. We can use the SmartFox.httpUploadURI property and pass it directly to the C# WebClient object.

In order to add custom parameters to the request, as we have seen in the HTML example, we can simply chain them as a query string to the httpUploadURI string, like this:

public void UploadTest()  
{	
	// Custom parameters
    string customParams="&__color=green&__random=42";

    // Create the WebClient object and send the file
    WebClient wc = new WebClient();
    wc.UploadFile(smartFox.httpUploadURI + customParams, "/path/to/upload/file.zip");
}
NOTE: don't forget to always start the custom parameter string with an & (ampersand) symbol and use the same symbol to concatenate all other parameters. Also don't forget to prefix each parameter name with the __ (double underscore).

» ActionScript 3

Using the FileReference class we can similarly upload any file to SFS2X.

private var fileRef:FileReference = new FileReference();

fileRef.addEventListener(Event.SELECT, onFileRefSelect);
fileRef.addEventListener(Event.COMPLETE, onFileRefComplete);

function onFileRefSelect(evt:Event):void
{
	trace("File selected: " + fileRef.name);
	
	var req:URLRequest = new URLRequest(smartFox.httpUploadURI);
	req.method = URLRequestMethod.POST;
	
	fileRef.upload(req);
}

function onFileRefComplete(evt:Event):void
{
	trace("Upload Complete!")
}

» Java

In Jave we highly recommend Apache's HttpClient library which offers a rich set of HTTP API to perform all sorts of operations including file upload.

private void testFileUpload()
{
	// Select a file to upload
	File uploadFile = new File("/path/to/upload/file.zip");
    
    // Prepare the upload encoding
    MultipartEntity entity = new MultipartEntity();
    entity.addPart("file", new FileBody(uploadFile));

    HttpPost request = new HttpPost(smartFox.getHttpUploadURI());
    request.setEntity(entity);
     
    HttpClient client = new DefaultHttpClient();
    try
    {
        HttpResponse response = client.execute(request);
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }
}

» HTML5 / Javascript

In HTML we can use two different modalities for uploading files. The regular HTML form-based option or a more modern, Ajax-based approach. The latter involves adding one or more third-party libraries to your project in order to be able to upload files.

In this article we have already seen how to use the form-based approach in the opening of this tutorial. We will need to point the format to this URL:

http://<server-ip>:<port>/BlueBox/SFS2XFileUpload

where server-ip is the SmartFoxServer address and port is the tcp port number used by the embedded Jetty server (default is 8080).

It is also mandatory to send a sessHashId parameter whose value must be the current "session token" obtained after logging in the server. In order to do this we can use JQuery to dynamically set the form field once the login event is received.

Let's take a look at this example:

var sfs = null;

function init()
{	
	// Create configuration object
	var config = {}
	config.host = "localhost"
	config.port = 8888
	config.zone = "BasicExamples"
	
	// Create SmartFox client instance
	sfs = new SmartFox(config)
	
	// Add event listeners
	sfs.addEventListener(SFS2X.SFSEvent.CONNECTION, onConnection, this)
	sfs.addEventListener(SFS2X.SFSEvent.LOGIN_ERROR, onLoginError, this)
    
    sfs.connect()
}

function onConnection(evt)
{
	if (evt.success)
		sfs.send(new SFS2X.Requests.System.LoginRequest(""))	
		
	else
		alert("Connection Failure!")
}

function onLogin(evt)
{
	// Set session token
	$("#sessHashId").val(sfs.sessionToken)
}

The HTML form to perform the upload is this:

<form action="http://localhost:8080/BlueBox/SFS2XFileUpload" method="post" enctype="multipart/form-data">
	<b>Select one or more files to upload:</b>
	<input type="file" multiple name="fileName"><br><br>
	<input type="hidden" name="sessHashId" value="" id="sessHashId">
	<input type="submit" value="Upload Files">
</form>

If you want to learn more about Ajax-based uploads we recommend checking these external resources:

 

» Handling the server side event

Let's now see how the upload event is handled in the server side extension.

This is the main Extension class:

public class UploadTestExtension extends SFSExtension
{
	@Override
	public void init()
    {
    	addEventHandler(SFSEventType.FILE_UPLOAD, UploadTestHandler.class);
    }
}

Next we create the server event handler:

public class UploadTestExtension extends BaseServerEventHandler
{
	@Override
	public void handleServerEvent(ISFSEvent event) throws SFSException
    {
    	User sender = (User) event.getParameter(SFSEventParam.USER);
        List<UploadedFile> upFiles = (List<UploadedFile>) event.getParameter(SFSEventParam.UPLOAD_FILE_LIST);
        Map<String, String> httpParams = (Map<String, String>) event.getParameter(SFSEventParam.UPLOAD_HTTP_PARAMS);
        
        String color = httpParams.get("__color");
        int random = Integer.parseInt(httpParams.get("__random"));
        
        // ...
    }
}
NOTE: The FILE_UPLOAD event is only received at the Zone Level.

» Moving and deleting files

We have mentioned that the uploaded files are momentarily parked in the temp "upload" folder with a random name to avoid file name collisions. At this point it's the developer responsibility to verify that the files are valid and withing the expected size ranges before moving them to the proper application folders.

If during the validation process on or more files don't seem to comply with the allowed formats or file sizes these files should be removed from the upload folder to avoid wasting disk space.

In order to either move or delete the uploaded files we will use the convenient FileUtils class from the Apache's common-io library bundled with SmartFoxServer 2X. For more details about the many useful methods in this class check their official documentation.

public class UploadTestExtension extends BaseServerEventHandler
{
	@Override
	public void handleServerEvent(ISFSEvent event) throws SFSException
    {
    	User sender = (User) event.getParameter(SFSEventParam.USER);
        List<UploadedFile> upFiles = (List<UploadedFile>) event.getParameter(SFSEventParam.UPLOAD_FILE_LIST);
        
        // Loop through uploaded files
		for (UploadedFile uf : upFiles)
		{
			File tempFile = new File(uf.getTempFilePath());
			
			if (isValidFile(tempFile))
			{
				// Valid file, move it to the destination folder
				File targetFile = "/path/to/destination/" + uf.getOriginalName();
                
                try
                {
					FileUtils.moveFile(tempFile, targetFile);
                }
                catch(IOException ioe)
                {
                	// Handle error...
                }
			}
			else
			{
				// Invalid file, remove from disk
                try
                {
					FileUtils.forceDelete(tempFile);
          		}
                catch(IOException ioe)
                {
                	// Handle error
                }
			}
		}
    }

	// Apply file validation criteria here...
	private boolean isValidFile(File file)
	{
		//...
	}
}

» Downloading the uploaded files

In order to render the uploaded files accessibile via HTTP, you will need to store them under the SFS2X/www/root/ folder which represents the root of the Jetty webserver embedded in SFS2X.

For example if you upload a file called image.png under SFS2X/www/root/ you will be able to download it via this url:

http://<server-domain>:8080/image.png

Any folder structure can be created under the root to organize your uploaded files.

» Security aspects

The ability to upload files can be activated on a per Zone basis, with the default setting turned off. From the AdminTool > ZoneSettings you can activate the FileUpload option.

As we have seen only logged in users will be able to upload files to the server and the FILE_UPLOAD event allows developers to add custom validation and logic for handling the files.

There is also an upload limit of 100MB per file or request, meaning that the client can upload one or more of files up to that size. For larger uploads the files will have to be split in chunks.