FTP Helper allows you to simply upload, download and append files using FTP.
Usage
Include the module and create a new connection.
local ftp = require("ftp") local connection = ftp.newConnection{ host = "domain.com", user = "username", password = "password", port = 21 -- Optional. Will default to 21. }
Upload a file from your resources directory, onSuccess and onError are optional event handlers.
connection:upload{ localFile = system.pathForFile("image.jpg", system.ResourcesDirectory), remoteFile = "/public_html/image.jpg", onSuccess = onUploadSuccess, onError = onError }
Download a file from into your documents directory and save it as "image.jpg", will either overwrite and existing file or create a new one.
connection:download{ remoteFile = "/public_html/image.jpg", localFile = "image.jpg", onSuccess = onDownloadSuccess, onError = onError }
Append the contents of one file on to another. Note, I know this works for text files but I have no idea what would happen if you tried it on binary files.
connection:append{ localFile = system.pathForFile("todaysLog.txt", system.ResourcesDirectory), remoteFile = "/public_html/completeLog.txt", onSuccess = onAppendSuccess, onError = onError }
Handlers
The onSuccess for uploading and downloading will have a parameter passed as "event.path", for uploading it is the remote path and downloading it is the path to your new file.
All onError handlers have "event.error" which is the FTP error code and message as passed from Lua sockets.
All Code (ftp.lua)
-- -- Abstract: Simplifies the use of FTP functions with Lua. -- -- Author: Graham Ranson - http://www.grahamranson.co.uk -- -- Version: 1.0 -- -- FTP Helper is MIT licensed, see http://developer.anscamobile.com/code/license -- Copyright (C) 2010 ANSCA Inc. All Rights Reserved. module(..., package.seeall) local ftp = require("socket.ftp") local ltn12 = require("ltn12") function newConnection(params) local self = {} self.host = params.host or "anonymous.org" self.user = params.user or "anonymous" self.password = params.password or "" self.port = params.port or 21 local putFile = function(params, command) success, error = ftp.put{ host = self.host, user = self.user, password = self.password, port = self.port, type = "i", step = ltn12.all, command = command, argument = params.remoteFile, source = ltn12.source.file( io.open( params.localFile, "rb" ) ) } if success then if params.onSuccess then params.onSuccess( { path = self.host .. params.remoteFile } ) end else if params.onError then params.onError( { error = error } ) end end return success, error end local getFile = function(params) local success, error = ftp.get{ host = self.host, user = self.user, password = self.password, port = self.port, type = "i", step = ltn12.all, command = command, argument = params.remoteFile, sink = ltn12.sink.file(params.localFile) } if success then if params.onSuccess then params.onSuccess( { path = params.localPath } ) end else if params.onError then params.onError( { error = error } ) end end return success, error end function self:upload(params) return putFile(params, "stor") end function self:download(params) params.localPath = system.pathForFile( params.localFile, system.DocumentsDirectory ) params.localFile = io.open( params.localPath, "w+b" ) return getFile(params) end function self:append(params) return putFile(params, "appe") end return self end
Example Usage
local ftp = require("ftp") local connection = ftp.newConnection{ host = "monkeydead.com", user = "monkeyde", password = "A4MUaC8wS8", port = 21 -- Optional. Will default to 21. } local onUploadSuccess = function(event) print("File uploaded to " .. event.path) end local onDownloadSuccess = function(event) print("File downloaded to " .. event.path) end local onAppendSuccess = function(event) print("File appended") end local onError = function(event) print("Error: " .. event.error) end connection:upload{ localFile = system.pathForFile("image.jpg", system.ResourcesDirectory), remoteFile = "/public_html/imagei.jpg", onSuccess = onUploadSuccess, onError = onError } connection:download{ remoteFile = "/public_html/image.jpg", localFile = "image2.jpg", onSuccess = onDownloadSuccess, onError = onError } connection:append{ localFile = system.pathForFile("todaysLog.txt", system.ResourcesDirectory), remoteFile = "/public_html/completeLog.txt", onSuccess = onAppendSuccess, onError = onError }
Notes
I have tested this on iOS but not Android.
I hope someone finds this useful.
At present I am not, this was only a quick thing and was "good enough" for me at the time. I will have a think and get back to you.
I m going to test it in Android devices (nexus one and Asus ee transformer) but I want more sample code about who you do the connection... could you show me a little bit more, please? thanks in advance
The connection is created by calling newConnection
1 2 3 4 5 6 7 8 | local ftp = require("ftp") local connection = ftp.newConnection{ host = "domain.com", user = "username", password = "password", port = 21 -- Optional. Will default to 21. } |
First of all, thanks for your quickly responds.
Then, when you have the connection, how do you upload a file? I saw the code you published but, It is all the possibilities(upload, download,...) but I don t see a main routine to call them and control the process.
If a transfer is broken, it could continue from the last position?
Best Regards
The description of the code at the top of this post shows how to upload a file ( as well as download and append ).
It does not do anything to deal with broken transfers, at a guess what you could probably do is encode the file into something like base64 and then upload that in small chunks keeping track of where you are. That way if it breaks you know which chunks are already up. You will then need to join and decode the chunks at the other end.
I have test it on Android devices and the emulator. On the emulator works ok, great! but on Android devices (Nexus One and Asus eePad Transformer) works only with text files (xml works ok for me) but not with binary files: if I transfer an JPG image or a PNG image it is created on the FTP server (FileZilla) with 0 Kbytes!!!
I thinked it could be the type of the ftp transfer. Initially the type is "i" (binary) and it seems to bee correct, but
I changed to type="a", doesn't work!
I changed to type="L 8", doesn't work!
My app must transfer an SQLite database so, I need the ftp to work...
Any ideas and suggestions are welcome!
I'm not sure what the difference is with Android as I haven't worked with it all that much however you could try skipping FTP altogether to upload your database files.
You could tray encoding the file contents in base64, POST that to a webserver, and the decode the result back into a file. Not entirely sure if it would work for database files but it works for text and image files.
Does anyone know how to NOT connect using PASV? For whatever reason it defaults to that and my server isn't behind a firewall.
I have a SQL file on my ftp server that I want to put more data in with my app.
I can do this by first downloading it, add som content and then upload it back again. The problem is that this method wont work if multiple users do the same thing at once. So is it possible to use connection:append in some way to upload data to the SQL file without first downloading the whole file?
It would be amazing if I could do something like this:
1 2 3 4 5 6 | connection:append{ data = [[INSERT INTO myTable (column1, column2) VALUES ("Value1", "Value2")]], remoteFile = "myAmazingDatabase.db", onSuccess = onAppendSuccess, onError = onError } |
As far as I am aware that wouldn't be possible however if you just want to modify the content of an SQL database the better way to do it would be to have some scripts, possibly PHP etc, running on your web server that can accept POST or GET requests and act accordingly.
Would this work:
I have two files on my ftp server, myDatabase.db and myPHP.php.
Then I have some scripts in my PHP file that can communicate with the db file? (Haven't learned any PHP so far)
Then in my app I use network.request( "ftp.myftpserver.com", "POST", networkListener, params) to add params to the PHP file which then sends that to the db file?
Is this possible?
Is it the best way of doing this on a ftp server?
How do I do it?
Also, when I upload a file I get an error. I've set onError to start a function that prints to the terminal but I can't see what the error is all about, how do I do that? When I look on my ftp server on my computer, the file is there but it's 0kb.
EDIT:
Never mind, fixed it. My mistake :)
For the former yes that is essentially what I do however I use mySQL rather than sqlite and as for the latter, glad you got it solved :-)
Okay great!
I wrote a php script but how do I test it?
Try inserting some data remotely? Or if you just want to make sure the script is getting called you could just print out some data in php and that will be the response of the network request.
I found some code on the internetz that I tried to use just to see if everything was working:
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 | <?php try { //create or open the database $database = new SQLiteDatabase('myDatabase.sqlite', 0666, $error); } catch(Exception $e) { die($error); } //add Movie table to database $query = 'CREATE TABLE Movies ' . '(Title TEXT, Director TEXT, Year INTEGER)'; if(!$database->queryExec($query, $error)) { die($error); } //insert data into database $query = 'INSERT INTO Movies (Title, Director, Year) ' . 'VALUES ("The Dark Knight", "Christopher Nolan", 2008); ' . 'INSERT INTO Movies (Title, Director, Year) ' . 'VALUES ("Cloverfield", "Matt Reeves", 2008); ' . 'INSERT INTO Movies (Title, Director, YEAR) ' . 'VALUES ("Beverly Hills Chihuahua", "Raja Gosnell", 2008)'; if(!$database->queryExec($query, $error)) { die($error); } //read data from database $query = "SELECT * FROM Movies"; if($result = $database->query($query, SQLITE_BOTH, $error)) { while($row = $result->fetch()) { print("Title: {$row['Title']} <br />" . "Director: {$row['Director']} <br />". "Year: {$row['Year']} <br /><br />"); } } else { die($error); } ?> |
I test it with XAMPP in my web browser and it works.
This creates a file for me which I put on my ftp server. Then in corona I download it but I get an error: file is encrypted or is not a database
Found a new example that worked :D
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 | <?php try { //open the database $db = new PDO('sqlite:dogsDb_PDO.sqlite'); //create the database $db->exec("CREATE TABLE Dogs (Id INTEGER PRIMARY KEY, Breed TEXT, Name TEXT, Age INTEGER)"); //insert some data... $db->exec("INSERT INTO Dogs (Breed, Name, Age) VALUES ('Labrador', 'Tank', 2);". "INSERT INTO Dogs (Breed, Name, Age) VALUES ('Husky', 'Glacier', 7); " . "INSERT INTO Dogs (Breed, Name, Age) VALUES ('Golden-Doodle', 'Ellie', 4);"); //now output the data to a simple html table... print "<table border=1>"; print "<tr><td>Id</td><td>Breed</td><td>Name</td><td>Age</td></tr>"; $result = $db->query('SELECT * FROM Dogs'); foreach($result as $row) { print "<tr><td>".$row['Id']."</td>"; print "<td>".$row['Breed']."</td>"; print "<td>".$row['Name']."</td>"; print "<td>".$row['Age']."</td></tr>"; } print "</table>"; // close the database connection $db = NULL; } catch(PDOException $e) { print 'Exception : '.$e->getMessage(); } ?> |
Though it works I would like to know why this works and the other one don't. Do you know?
And how do I go further with this?
Let's say that I use the exact table and data above, what should I write inside the network.request() brackets if I want to add another row of data remotely from the app, if both the php file and sqlite file are on the same ftp server and in the same folder?
You would want to create a PHP script that can take data in a POST variable and then pass whatever data you want through the network.request call. Your PHP would then have to deal with the data and decided what it needs to do.
Do you got any good example on how to make a PHP script that can take data in a POST variable?
This has some nice short info on both POST and GET variables - http://www.tizag.com/phpT/postget.php
I found a piece of code here on the forums and it works, but how do I write it with network.request() ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | http = require("socket.http") ltn12 = require("ltn12") url = require("socket.url") local post = "InputName=" .. "Robot7001" post = post .. "&InputAge=" .. "8" local response = {} local r, c, h = http.request { url = "https://www.yoursite.com/TestPost.php?", method = "POST", headers = { ["content-length"] = #post, ["Content-Type"] = "application/x-www-form-urlencoded" }, source = ltn12.source.string(post), sink = ltn12.sink.table(response) } print(response[1]) |
Sure I can use this code but I would like to understand the network.request function better :)
How do I use the code above to POST to a php and sqlite file on a ftp server?
The page here has a bit showing how to send data via POST - http://developer.anscamobile.com/reference/index/networkrequest
Well actually I dont need to use network.request since the last code that I wrote here works.
But how would I write my current code if I want to access a php file on a ftp server?
Sorry I don't really have the time to write out the code for you, all I can really do is point you in the right places and that way you'll better understand your resulting code anyway.
That's more than enough for me, thanks! :)
If you have any specific questions then I can try to help, but can't guarantee anything :-)
Thanks! :)
Any idea how to make this use SSL? It's pretty easy to sniff out upload credentials without.