Tutorial: Uploading Files Demystified

Uploading files to a web server can be a tricky process because a lot depends on the web server’s features, abilities, and limits. One might think that there’s a “standard” upload method, but unfortunately there’s not. How an Apache server running PHP handles things could be drastically different than a .NET server running Visual Basic or C#. Even within a family of servers sharing a common language, the rules vary from server to server and from upload script to upload script.

You must also be very careful about which files should be allowed to upload to your web server. Since you’re creating a script that anybody with the URL can run — and effectively send data to your server — you may open your server to being hacked unless you utilize caution and care. It’s quite easy with server-side scripts to allow the ability to write over critical files with malicious code, so if you don’t know what you’re doing on the server side, you may want to consider other options. In addition, your server may enforce limits on your upload abilities that are beyond your control. For example, many servers limit uploads to small files (under 2 MB), or they may limit the number of files that can be uploaded. All of these limitations must be considered before you begin implementing a file upload process.

Before You Begin

Before you proceed with this tutorial, you should read this manual to gain a clearer understanding of the entire process. This tutorial presents a “quick and dirty” method to upload files, but before you use this in production, you must make some adjustments and secure the script as necessary. You should anticipate that it’ll take some effort to get this right!

Using “network.upload()”

Corona’s network.* library contains an API called network.upload() which is a simple method for uploading files to a server, assuming that your server handles the simple method of uploading files using HTTP PUT. Most existing scripts that accept file upload probably won’t work with network.upload() because they’re looking for an HTTP POST form-based MIME multi-part upload format. Corona’s network.upload() API does not talk to these kinds of scripts, but we’ll discuss this further in a bit. First, let’s look at the Corona side…

The Corona Script

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

--Callback functiontohandle the upload events that are generated.

--There will be several events:one toindicate the start andendof the

--process andseveral toindicate the progress(depends on the file size).

--Always test foryour error conditions!

local functionuploadListener(event)

if(event.isError)then

print("Network Error.")

--Thisislikelyatime out orserver being down.Inother words,

--It was unable tocommunicate with the web server.Now ifthe

--connection tothe web server worked,but the request isbad,this

--will be falseandyou need tolook at event.status andevent.response

--tosee why the web server failed todowhat you want.

else

if(event.phase=="began")then

print("Upload started")

elseif(event.phase=="progress")then

print("Uploading... bytes transferred ",event.bytesTransferred)

elseif(event.phase=="ended")then

print("Upload ended...")

print("Status:",event.status)

print("Response:",event.response)

end

end

end

--Sepcify the URL of the PHP script toupload to.Dothison your own server.

--Also define the method as"PUT".

local url="http://yourwebserver.com/somepath/upload.php"

local method="PUT"

--Set some reasonable parameters forthe upload process:

local params=

timeout=60,

progress=true,

bodyType="binary"

--Specify what file toupload andwhere toupload it from.

--Also,set the MIME type of the file so that the server knows what toexpect.

local filename="image.jpg"

local baseDirectory=system.ResourceDirectory

local contentType="image/jpeg"--another option is"text/plain"

--There isno standard way of using HTTP PUT totell the remote host what

--toname the file.We'll make up our own header here so that our PHP script

-- expects to look for that and provides the name of the file. Your PHP script

-- needs to be "hardened" because this is a security risk. For example, someone

-- could pass in a path name that might try to write arbitrary files to your

The Script Demystified…

As with all network.* API calls, this operates asynchronously, meaning that it will return to your program immediately and process the upload in the background. However, you need to know the status of the upload, in particular when it completes, which is handled by the event listener function:

Let’s inspect this function in more detail. The first thing you must check is whether a network error occurred. This is returned in the event.isError attribute. In network programming, it’s important to understand that this error may indicate that the server is down, it’s unreachable, or it’s timing out. Effectively, it means that you never successfully communicated with the server.

You can successfully connect to and interact with the web server, asking it to do something that it can’t, but it will report a “success” in regards to the isError attribute. In other words, regardless of whether the server sends you a “right” or “wrong” response, you technically had a successful transaction with it. Thus, the else condition block is where you can inspect and handle the various phases of the upload. For instance, in the "began" phase, you may choose to display a widget.newProgressView(). Then in the "progress" phase, increment that widget’s status based on the amount of bytes transmitted. Finally, the "ended" phase lets you know that the web server has completed the upload process.

All finished, correct? Not so fast! The file may still have failed to upload for various reasons, for example, the file was too large, it was not a valid file name, or some permission issue on the server prevented it from uploading. Thus, you should check the event.status attribute which will hold the HTTP “result” code, such as 201 (success) or 403 (permission denied). Depending on the server, there may be additional information in the event.response attribute that can indicate where the problem resided.

Now, let’s re-inspect the various variables and tables required for the network.upload() API call. These include:

The URL of the server script to execute.

The HTTP method (in this case we’re going to use “PUT”).

Some parameters to configure the API Call, including the timeout, body type, and whether you want progress updates.

The filename to upload.

The source directory where the file can be found.

A MIME type string indicating the file type for the server’s use.

A custom header for the PHP script (we’ll discuss this further down).

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

--Sepcify the URL of the PHP script toupload to.Dothison your own server.

--Also define the method as"PUT".

local url="http://yourwebserver.com/somepath/upload.php"

local method="PUT"

--Set some reasonable parameters forthe upload process:

local params=

timeout=60,

progress=true,

bodyType="binary"

--Specify what file toupload andwhere toupload it from.

--Also,set the MIME type of the file so that the server knows what toexpect.

local filename="image.jpg"

local baseDirectory=system.ResourceDirectory

local contentType="image/jpeg"--another option is"text/plain"

local headers={}

headers.filename=filename

params.headers=headers

Because this method has no way of telling the server the name of the file to save, we must create a special header named filename that will contain that name (the file we want to use on the remote server). With this step done, add the headers table to the params table and then call network.upload():

The PHP Script

For usage with this tutorial, we’re providing a sample PHP script for download. This script is fairly simple and straightforward, but unless you know PHP, it will look intimidating. Before you attempt to modify it for your own project, there are a few important things to understand:

1. Security

When you write a script online, it’s 100% your responsibility to ensure that it cannot be easily hacked. Scripts that write files to the server are the most vulnerable. You may think that only your app will use it, but once it’s on a website, anybody can execute it, figure out the parameters and the methods, and find holes to exploit. We’ve taken some basic steps to prevent this, but you need to take it the rest of the way. This script lets the caller set the filename which is inherently dangerous since people can put in tricks to create false paths that may let them write files to arbitrary locations. Your script should never run at elevated permissions, and your file system should be read-only wherever there are executable script files or system binary executables. Finally, you should do your best to scrub any provided file name before putting this code into production. The safest thing is to prevent the caller from specifying the filename, but this can create usability issues in regards to keeping the files organized.

2. Know Your Server’s Limits

Many PHP servers, if they allow uploads at all, will have very tight limits on the file size that can be uploaded. The code above has a size limit check, but it only works if the server allows larger files and you want to limit the size. In this sample, we allow up to a 5 MB file, but the server itself may only allow 1 MB. The server may not even reach your script if the file is too large, so it’s your responsibility to control the limits. Many websites live in a shared environment with other websites and you should be a good network citizen!

3. Handling Files of the Same Name

The code above uses a simple (but somewhat flawed) method of adding an increasing sequence number to the end of the string. It stops incrementing the number at 100. The while loop that checks for duplicate file names can’t run forever and after you hit 100 uploads of the same name, it will start overwriting the 100th file. Again, this is not production-ready script and you must adapt it to your needs. Another potential method is to get a list of files, find the one with the highest number, parse the name from the number, and increment it.

4. The Upload Directory

This script assumes that you’ll create a folder named upload as a child folder of where this script is located, but this may not be the best option for your website. This upload folder must have write permissions for the ID your web server runs at. Generally the web server will not run with an ID or within a group that your account has access to. This means the folder needs to be World writable. As a safety precaution, you probably don’t want the folder to have READ or EXECUTE/LIST privileges for the World user. Finally, your server may need to dump the files in a completely different location in your server tree, so you must figure out that path and adjust the PHP script accordingly.

In Summary

As you can see, the act of uploading files can be a complex task. Hopefully this tutorial has shed some light on the process. As always, please leave your questions and comments below. You may also download the PHP script based on this tutorial if you wish to use it as a foundation for your own implementation.

Disclaimer: SDKNews.com only syndicates the blog entries from various SDK blogs.
We are not the creator/author of these entries (posts). Product names, brand names
and company names mentioned on this site may be trademarks of their respective owners.