Uploading images to server

Hello!

I have managed to upload images with network.upload but I would like to do it with network.request and post method.

I’m a bit lost and don’t event know where to start.

Any help would be appreciated :slight_smile:

Why would you not want to use network.upload for files? It supports post method

Helpful link

1 Like

Hi @Scott_Harrison and thank you for helping!

I have used the docs and the tutorial and have successfully uploaded images using exactly the code provided.

But with this approach anyone can upload images to the folder if knows the path.
To avoid that, as a simple, fast and easy solution, at the top of the php file that uploads the image I have this code

// check login
$getsessionid = $_POST["sessionidsent"];
if ($getsessionid == "hello123") {
    echo "You are allowed to upload image";
	} else {
    echo "Not allowed to upload image.";
	header("Location: error.php");
	exit();
}

My problem is that I don’t know how to convert the lua code in order to sent the $_POST[“sessionidsent”].
I have red in this forum that network.upload doesn’t support “body”, so how am I going to do it?

I thought that I could use post and network.request because I know how to sent the $_POST[“sessionidsent”].

local sessionidsentPSW = "hello123"
local sessionidsentB = "&sessionidsent="..sessionidsentPSW                              
			
local headers = {}
  	
headers["Content-Type"] = "application/x-www-form-urlencoded"
headers["Accept-Language"] = "en-US"
 	
local body = sessionidsentB
		
local params = {}
params.headers = headers
params.body = body
				
network.request( "https://www.mysite.com/uploadme.php", "POST", networkListener, params )

The code above is working but I don’t know how to send image!

I have created a nice login system and upload / retrieve data from database but uploading an image without everyone able to do it without permission is giving me a hard time :slight_smile:

I would check out this 3rd module for uploading a file with body

Edit: I scroll down a bit you can see how to use this with PHP

2 Likes

Yesterday I found the multipartForm.lua but I was getting (and still get) this error and I don’t know how to fix it:

ERROR: Runtime error
class_MultipartFormData.lua:102: assertion failed!
stack traceback:
[C]: in function ‘assert’
class_MultipartFormData.lua:102: in function ‘getBody’
main.lua:215: in main chunk

I have spend so many hours trying to find a solution but I had no idea about the PHP code!
Thank you so much :slight_smile:

Full code of class_MultipartFormData.lua

module(..., package.seeall)
-------------------------------------------------
--
-- class_MultipartFormData.lua
-- Author: Brill Pappin, Sixgreen Labs Inc.
--
-- Generates multipart form-data for http POST calls that require it.
--
-- Caution: 
-- In Corona SDK you have to set a string as the body, which means that the 
-- entire POST needs to be encoded as a string, including any files you attach.
-- Needless to say, if the file you are sending is large, it''s going to use 
-- up all your available memory!
--
-- Example:
--[[
local MultipartFormData = require("class_MultipartFormData")
 
local multipart = MultipartFormData.new()
multipart:addHeader("Customer-Header", "Custom Header Value")
multipart:addField("myFieldName","myFieldValue")
multipart:addField("banana","yellow")
multipart:addFile("myfile", system.pathForFile( "myfile.jpg", system.DocumentsDirectory ), "image/jpeg", "myfile.jpg")
 
local params = {}
params.body = multipart:getBody() -- Must call getBody() first!
params.headers = multipart:getHeaders() -- Headers not valid until getBody() is called.
 
local function networkListener( event )
        if ( event.isError ) then
                print( "Network error!")
        else
                print ( "RESPONSE: " .. event.response )
        end
end
 
network.request( "http://www.example.com", "POST", networkListener, params)
 
]]
 
-------------------------------------------------
local crypto = require("crypto")
local ltn12 = require("ltn12")
local mime = require("mime")
 
MultipartFormData = {}
local MultipartFormData_mt = { __index = MultipartFormData }
 
function MultipartFormData.new()  -- The constructor
        local newBoundary = "MPFD-"..crypto.digest( crypto.sha1, "MultipartFormData"..tostring(object)..tostring(os.time())..tostring(os.clock()), false )
        local object = { 
                isClass = true,
                boundary = newBoundary,
                headers = {},
                elements = {},
        }
  
        object.headers["MIME-Version"] = "1.0" 
  
  return setmetatable( object, MultipartFormData_mt )
end
 
function MultipartFormData:getBody()
        local src = {}
        
        -- always need two CRLF's as the beginning
        table.insert(src, ltn12.source.chain(ltn12.source.string("\n\n"), mime.normalize()))
        
        for i = 1, #self.elements do
                local el = self.elements[i]
                if el then
                        if el.intent == "field" then
                                local elData = {
                                        "--"..self.boundary.."\n",
                                        "content-disposition: form-data; name=\"",
                                        el.name,
                                        "\"\n\n",
                                        el.value,
                                        "\n"
                                }
                                
                                local elBody = table.concat(elData)
                                table.insert(src, ltn12.source.chain(ltn12.source.string(elBody), mime.normalize()))
                        elseif el.intent == "file" then
                                local elData = {
                                        "--"..self.boundary.."\n",
                                        "content-disposition: form-data; name=\"",
                                        el.name,
                                        "\"; filename=\"",
                                        el.filename,
                                        "\"\n",
                                        "Content-Type: ",
                                        el.mimetype,
                                        "\n",
                                        "Content-Transfer-Encoding: ",
                                        el.encoding,
                                        "\n\n",
                                }
                                local elHeader = table.concat(elData)
                                
                                local elFile = io.open( el.path, "rb" )
                                assert(elFile)
                                local fileSource = ltn12.source.cat(
                                                        ltn12.source.chain(ltn12.source.string(elHeader), mime.normalize()),
                                                        ltn12.source.chain(
                                                                        ltn12.source.file(elFile), 
                                                                        ltn12.filter.chain(
                                                                                mime.encode(el.encoding), 
                                                                                mime.wrap()
                                                                        )
                                                                ),
                                                        ltn12.source.chain(ltn12.source.string("\n"), mime.normalize())
                                                )
                                
                                table.insert(src, fileSource)
                        end
                end
        end
        
        -- always need to end the body
        table.insert(src, ltn12.source.chain(ltn12.source.string("\n--"..self.boundary.."--\n"), mime.normalize()))
        
        local source = ltn12.source.empty()
        for i = 1, #src do
                source = ltn12.source.cat(source, src[i])
        end
        
        local sink, data = ltn12.sink.table()
        ltn12.pump.all(source,sink)     
        local body = table.concat(data)
        
        -- update the headers we now know how to add based on the multipart data we just generated.
        self.headers["Content-Type"] = "multipart/form-data; boundary="..self.boundary
        self.headers["Content-Length"] = string.len(body) -- must be total length of body
        
        return body
end
 
function MultipartFormData:getHeaders()
        assert(self.headers["Content-Type"])
        assert(self.headers["Content-Length"])
        return self.headers
end
 
function MultipartFormData:addHeader(name, value)
        self.headers[name] = value
end
 
function MultipartFormData:setBoundry(string)
        self.boundary = string
end
 
function MultipartFormData:addField(name, value)
        self:add("field", name, value)
end
 
function MultipartFormData:addFile(name, path, mimeType, remoteFileName)
        -- For Corona, we can really only use base64 as a simple binary 
        -- won't work with their network.request method.
        local element = {intent="file", name=name, path=path, 
                mimetype = mimeType, filename = remoteFileName, encoding = "base64"}
        self:addElement(element)
end
 
function MultipartFormData:add(intent, name, value)
        local element = {intent=intent, name=name, value=value}
        self:addElement(element)
end
 
function MultipartFormData:addElement(element)
        table.insert(self.elements, element)
end
 
function MultipartFormData:toString()
        return "MultipartFormData [elementCount:"..tostring(#self.elements)..", headerCount:"..tostring(#self.headers).."]" 
end
 
return MultipartFormData

Ok, got some hope back.

I replaced this

local elFile = io.open( el.path, "rb" )`

With this and error is gone

 local path = system.pathForFile( "myfile.jpg", system.baseDirectory )
 local elFile = io.open( path, "rb" )

Got response from the server too :smiley:
Needs work but when I make it I will post the solution.

Mission complete!

I’m posting the only code missing (although its available in Scott’s archive link)

<?php
$target_path = "uploadfolder/";
 
$target_path = $target_path . basename( $_FILES['myfile']['name']); 
 
if(move_uploaded_file($_FILES['myfile']['tmp_name'], $target_path)) {
        $src = $target_path;
        $dst = 'uploadfolder/decoded_'.basename( $_FILES['myfile']['name']);
        base64file_decode( $src, $dst );
    echo "The file ".  basename( $_FILES['myfile']['name']). " has been uploaded";
} else{
    echo "There was an error uploading the file, please try again!";
}
 
function base64file_decode( $inputfile, $outputfile ) { 
  /* read data (binary) */ 
  $ifp = fopen( $inputfile, "rb" ); 
  $srcData = fread( $ifp, filesize( $inputfile ) ); 
  fclose( $ifp ); 
  /* encode & write data (binary) */ 
  $ifp = fopen( $outputfile, "wb" ); 
  fwrite( $ifp, base64_decode( $srcData ) ); 
  fclose( $ifp ); 
  /* return output filename */ 
  return( $outputfile ); 
} 
?>

Also a few notes

The image file is base64 encoded.
Two images are getting upload, encoded and decoded version.
If you need only the decoded version replace this:
$dst = ‘uploadfolder/decoded_’.basename( $_FILES[‘myfile’][‘name’]);
with this:
$dst = ‘uploadfolder/’.basename( $_FILES[‘myfile’][‘name’]);

You can change the upload file size limit on your server by editing php.ini.

Everything works great on simulator console but on android you also need to create and xml folder in androidresources/res folder and create file network_security_config.xml with this code:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">domainname.com</domain>
    </domain-config>
</network-security-config>

Last but not least I didn’t find a way to upload an image from a file that was already inside the apk when built.
There are a few solutions here:

I tried the easiest workaround by renaming the file but did’t work for me.
The truth is that didn’t try much (didn’t even read all the posts :smiley: because all I need is to upload files from image gallery / camera.

The most important thing is that Solar2D proved once again that is an amazing tool and you can do (almost) anything you want.

@Scott_Harrison thanks again your help and @vlads keep up the good work :smiley:

1 Like

Just to chime in, be very careful about how you implement the server side logic.

It seems that you aren’t validating the uploads in any way nor do you require any account/credentials to upload. Furthermore, if I’m reading this right, you are also sending these over unsecured http traffic, so anyone can read all of the contents.

This leaves you open to all kinds of mischief and attacks.

1 Like