Trying to upload an image and getting an error

I am creating a social app that let’s the user upload a profile picture . When I go to the profile.lua file (where the user will upload their profile pic) I immediately get this error :

\class\_MultipartFormData.lua:102: assertion failed!

I tried making the function global but that didn’t change .

profile.lua:

local MultipartFormData = require("class\_MultipartFormData") local multipart = MultipartFormData.new() local path=system.pathForFile( "image.jpg", system.TemporaryDirectory ) multipart:addFile("Image", path, "image/jpg", "image.jpg") local params = {} params.body = multipart:getBody() params.headers = multipart:getHeaders() -- Headers not valid until getBody() is called. network.request("http://hash.host22.com/upload.php", "POST", listener, params)

class_MultipartFormData.lua:

https://github.com/benglard/Augment/blob/master/App/class_MultipartFormData.lua

also would it be better if I saved the file in a folder in my corona app and then send it to the database ?

You can just upload the image without using that plugin. I did mine just by using the network.* function, and post it to the php-server.

Depending on the server, using HTTP POST with  Multi-part time may be required. In fact it’s the most popular file upload method.

Looking at the MultipartFormData line 101 and 102 you will find:

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

“assert” says to generate an error if the passed parameter is nil. This is telling you that elFile is nil. Looking at line 101, how can elFile be nil? If el.path doesn’t exist. Tracing this back el.path is the second parameter you pass to:

multipart:addFile("Image", path, "image/jpg", "image.jpg")

You should a) print out path and make sure it’s what you expect and b) make sure that image actually exists. You’re constructing the path here:

local path=system.pathForFile( "image.jpg", system.TemporaryDirectory )

How is image.jpg getting into system.TemporaryDirectory?  Are you sure it’s named “image.jpg” and not “image.png” or some other file name?

Rob

I did this and I’m still getting the same error :

local MultipartFormData = require("class\_MultipartFormData") local multipart = MultipartFormData.new() local path=system.pathForFile( "bg1.png", system.TemporaryDirectory ) multipart:addFile("Image", path, "images/uploads", "bg1.png") local params = {} params.body = multipart:getBody() params.headers = multipart:getHeaders() -- Headers not valid until getBody() is called.

Also Can I upload the image straight to the server(as stated above) with 000webhost ?

Does bg1.png exist? Is it in the system.temporaryDirectory?

How are you getting the photo from the user to upload?

You can’t upload the file to any server until you have one to upload. I have no clue what 000webhost’s facilities are. It’s likely determine by whatever upload.php is expecting.  Without knowing that script or seeing the documentation for it no one can really answer that question

Rob

Honestly I don’t even know where that is 

But image does exist .

What makes this problem difficult for any one to help you with is we can’t see everything going on and you’ve not shared with us how your image is getting into system.TemporaryDirectory.

All I can tell you is why you are getting the error. You can put in print statements, look in your console log and make sure you’re getting expected values.

We know nothing about upload.php or it’s expectations, but until you solve your multipart mime problem we can’t even start to look at the upload.php script. Though if that’s your script and you have the code  for it, maybe you don’t need the multipart mime to upload,

Let’s keep this simple. How does the profile get into system.TemporaryDirectory in the first place?

What is system.TemporaryDirectory ?

Corona apps run in a protected area and can only access it’s own files. This is called the “app sandbox”. There are five folders your app has access to in your App sandbox: 

system.ResourceDirectory – Where your files like main.lua reside. This is a READ ONLY folder. You cannot write files here and there are some additional rules for Android apps.

system.DocumentsDirectory – Where you can store important files for your app. You can both read from and write to this folder. You should only put important, long term files here.

system.CachesDirectory – Where you can store files that you download from the Internet. You can read/write this folder but be aware if the system runs low on disk space, files in this folder could be removed by the system with out warning. They came from the Internet so you can always re-download it.

system.TemporaryDirectory – Like system.CachesDirectory, this is a read-write folder with no guarantee that files will stay there. The system can clean it up whenever.

system.ApplicationSupportDirectory – a place to save settings files though most use system.DocumentsDirectory for this.

Because this is your app’s “sandbox”, the only way files get into any of those folders is if your apps code creates and saves them there (system.ResourceDirectory aside, since that’s the files in your app’s program folder).

For your bg1.png file or image.jpg file to be in system.TemporaryDirectory is if you have code that copies the file there from somewhere. As the programmer, you should know the process that makes this happen. If this doesn’t sound familiar, then it’s because you are not putting a file in system.TemporaryDirectory for the multi-part mime function to be able to read.

So I will re-ask the question, how are  you getting an image into system.TemporaryDirectory?

Rob

With the code I have .

You have shown us no code though.  Please show us the code where you are putting your image in system.TemporaryDirectory.

I thought this was the code :

local MultipartFormData = require("class\_MultipartFormData") local multipart = MultipartFormData.new() local path=system.pathForFile( "bg1.png", system.TemporaryDirectory ) multipart:addFile("Image", path, "images/uploads", "bg1.png") local params = {} params.body = multipart:getBody() params.headers = multipart:getHeaders() -- Headers not valid until getBody() is called. network.request("http://hash.host22.com/upload.php", "POST", listener, params)

That code’s job is to take a file named bg1.png that’s already in system.TemporaryDirectory and sends it to a script named upload.php and nothing more. It does nothing to let the user select a photo from their camera or photo album and copy it into your app’s sandbox so you can upload it.

I’m pretty sure, based on this conversation that there is no file named bg1.png in the system.TemporaryDirectory and that’s what’s causing your error.

Rob

This code lets the user select the picture :

 -- Selection completion listener local function onComplete( event ) local photo = event.target if photo then print( "photo w,h = " .. photo.width .. "," .. photo.height ) end end local button = widget.newButton( { shape = "roundedRect", left = 70, top = 350, id = "pfp", label = "Upload picture", onEvent = pickPhoto, fillColor = { default={ 1, 0.2, 0.5, 0.7 }, over={ 1, 0.2, 0.5, 1 } }, labelColor = { default={ 2, 4, 1.5 }, over={ 2, 5, 1.5, 2.2 } } } ) local function pickPhoto( event ) media.selectPhoto( { mediaSource = media.SavedPhotosAlbum, listener = onComplete, origin = button.contentBounds, permittedArrowDirections = { "right" }, destination = { baseDir=system.TemporaryDirectory, filename="image.jpg" } }) end

And is this working for you? (Hint, it should not). When you define your button,  pickPhoto is nil. So pressing your button won’t ever call your  pickPhoto function. This is a scope issue. You have to create your button after you create your pickPhoto function.

Now when that works, and it brings up the photo album and you select a photo, then it will save “image.jpg” to system.TemporaryDirectory. At that point the listener function  onComplete will get called. That is where you should call your code to upload your image.

Rob

I put the button after the pickPhoto function and I’m still getting the same error .

I hate to ask such simplistic questions, but you are pressing the button and selecting a photo and choosing to use it right? Can you post your latest code (both the select photo and the upload code)? 

You need a workflow like this:

Press select photo button ->  User selects photo and chooses it to use it ->  photo is saved in system.TemporaryDirectory and the callback function is called -> Call back function calls your upload function.

If you’re calling your upload function without selecting the photo it won’t work.

Rob

I can’t click the button because once I go to the profile.lua file I immediately get the error I am having now .

local MultipartFormData = require("class\_MultipartFormData") local multipart = MultipartFormData.new() local path=system.pathForFile( "bg1.png", system.TemporaryDirectory ) multipart:addFile("Image", path, "images/uploads", "bg1.png") local params = {} params.body = multipart:getBody() params.headers = multipart:getHeaders() -- Headers not valid until getBody() is called. network.request("http://hash.host22.com/upload.php", "POST", listener, params) local userName = composer.getVariable( "username" ) local function networkListener( event ) if ( event.isError ) then local alert = native.showAlert( "Error Loading .", "Check your internet connection .", { "Try again" } ) end end -- Selection completion listener local function onComplete( event ) local photo = event.target if photo then print( "photo w,h = " .. photo.width .. "," .. photo.height ) end end local function pickPhoto( event ) media.selectPhoto( { mediaSource = media.SavedPhotosAlbum, listener = onComplete, origin = button.contentBounds, permittedArrowDirections = { "right" }, destination = { baseDir=system.TemporaryDirectory, filename="image.jpg" } }) end local button = widget.newButton( { shape = "roundedRect", left = 70, top = 350, id = "pfp", label = "Upload picture", onEvent = pickPhoto, fillColor = { default={ 1, 0.2, 0.5, 0.7 }, over={ 1, 0.2, 0.5, 1 } }, labelColor = { default={ 2, 4, 1.5 }, over={ 2, 5, 1.5, 2.2 } } } )
local MultipartFormData = require("class\_MultipartFormData") local userName = composer.getVariable( "username" ) local function networkListener( event ) if ( event.isError ) then local alert = native.showAlert( "Error Loading .", "Check your internet connection .", { "Try again" } ) end end -- Selection completion listener local function onComplete( event ) local photo = event.target if photo then print( "photo w,h = " .. photo.width .. "," .. photo.height ) local multipart = MultipartFormData.new() local path=system.pathForFile( "bg1.png", system.TemporaryDirectory ) multipart:addFile("Image", path, "images/uploads", "image.jpg") local params = {} params.body = multipart:getBody() params.headers = multipart:getHeaders() -- Headers not valid until getBody() is called. network.request("http://hash.host22.com/upload.php", "POST", networkListener, params) end end local function pickPhoto( event ) media.selectPhoto( { mediaSource = media.SavedPhotosAlbum, listener = onComplete, origin = button.contentBounds, permittedArrowDirections = { "right" }, destination = { baseDir=system.TemporaryDirectory, filename="image.jpg" } }) end local button = widget.newButton( { shape = "roundedRect", left = 70, top = 350, id = "pfp", label = "Upload picture", onEvent = pickPhoto, fillColor = { default={ 1, 0.2, 0.5, 0.7 }, over={ 1, 0.2, 0.5, 1 } }, labelColor = { default={ 2, 4, 1.5 }, over={ 2, 5, 1.5, 2.2 } } } )

You can’t just call the upload code when the module loads because you haven’t let the user select a photo yet. As I mentioned above you have to call that code inside the listener function for selecting the photo. Order of operations matter.

Also you are still trying to load a file named bg1.png which does not exist in system.TemporaryDirectory since you have no code to put a file named bg1.png in that specific location.  Your code to get a photo from the photo album saves a file named “image.jpg” to system.TemporaryDirectory. That’s the file you should try and upload.

Now you are likely going to run into another problem by naming all of your profile images “image.jpg”. If you have 1000 users, you will have 1000 “image.jpg” files that are likely going to write over top of each other.

I would do something like this instead:

local MultipartFormData = require("class\_MultipartFormData") local userName = composer.getVariable( "username" ) local function networkListener( event ) if ( event.isError ) then local alert = native.showAlert( "Error Loading .", "Check your internet connection .", { "Try again" } ) end end -- Selection completion listener local function onComplete( event ) local photo = event.target if photo then print( "photo w,h = " .. photo.width .. "," .. photo.height ) local multipart = MultipartFormData.new() local path=system.pathForFile( "bg1.png", system.TemporaryDirectory ) multipart:addFile("Image", path, "images/uploads", userName .. ".jpg") local params = {} params.body = multipart:getBody() params.headers = multipart:getHeaders() -- Headers not valid until getBody() is called. network.request("http://hash.host22.com/upload.php", "POST", networkListener, params) end end local function pickPhoto( event ) media.selectPhoto( { mediaSource = media.SavedPhotosAlbum, listener = onComplete, origin = button.contentBounds, permittedArrowDirections = { "right" }, destination = { baseDir=system.TemporaryDirectory, filename= userName .. ".jpg" } }) end local button = widget.newButton( { shape = "roundedRect", left = 70, top = 350, id = "pfp", label = "Upload picture", onEvent = pickPhoto, fillColor = { default={ 1, 0.2, 0.5, 0.7 }, over={ 1, 0.2, 0.5, 1 } }, labelColor = { default={ 2, 4, 1.5 }, over={ 2, 5, 1.5, 2.2 } } } )

that way your files on your server have unique names based on your user’s userName.

Rob