Trying to upload an image and getting an error

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

I kept getting this error :

attempt to index global 'button' (a nil value)

I had two buttons with the same exact values so I thought that was the problem . When I removed one I was able to click the button and select a photo . When I selected the photo , I went back to the profile.lua file and nothing happened . Then I clicked the profile.lua tab and I got this error :

profile.lua:116 attempt to call method 'addEventListener' (a nil value) 

What’s wrong with it ?

local composer = require( "composer" ) local scene = composer.newScene() local widget = require("widget") -- forward declare the text fields local json = require("json") local button 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 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 } } } ) function scene:create(event) local screenGroup = self.view local background = display.newImageRect("insta.jpg",display.contentWidth,display.contentHeight) background.x = display.contentCenterX background.y = display.contentCenterY screenGroup:insert(background) local passedInParams = event.params --\<------ important. This is how you get the passed values local userNameText = display.newText(userName, 160, 200, native.systemFont, 30 ) userNameText:setFillColor( 1, 0, 0 ) screenGroup:insert(userNameText) end local tabButtons = { { label = "#News Feed", width = 52, height = 10, id = "newsfeed", size = 16, onPress = function() composer.gotoScene("newsfeed"); end, selected = true }, { label = "#Profile", size = 16, id = "profile", onPress = function() composer.gotoScene("profile"); end, selected = true } } -- Create the widget local tabBar = widget.newTabBar( { top = display.contentHeight -52, width = display.contentWidth, buttons = tabButtons, } ) function scene:show(event) local phase = event.phase if ( phase == "will" ) then print("Phase started") button:addEventListener( "tap", pickPhoto ) elseif ( phase == "did" ) then print("phase on login") end composer.removeScene( "login" ) end scene:addEventListener( "show" ) function scene:hide(event) local phase = event.phase if ( phase == "will" ) then print("Phase started") button:removeEventListener( "tap", pickPhoto ) display.remove(button) elseif ( phase == "did" ) then print("phase on login") end end function scene:destroy(event) end scene:addEventListener("create", scene) scene:addEventListener("show", scene) scene:addEventListener("hide", scene) scene:addEventListener("destroy", scene) return scene

“button”, “tabBar” and “tabButtons” should all be inside scene:create().  “button” and “tabBar” should be added to your scene group.

A button widget does not use a :addEventListener() or does it need a :removeEventListener() that’s handled internally by the widget. If you add the button to your scene’s view group, you won’t need to delete it in scene:hide()

Ok so … Where does the image go ? I have a images/uploads path but there is nothing there . How do I make it go there ?

Also is it only limited to jpg images ? If so can I change it to any picture ? GIF PNG etc ?

This block of code:

 media.selectPhoto( { mediaSource = media.SavedPhotosAlbum, listener = onComplete, origin = button.contentBounds, permittedArrowDirections = { "right" }, destination = { baseDir=system.TemporaryDirectory, filename= userName .. ".jpg" } })

The destination = { baseDir=system.TemporaryDirectory, filename= userName … “.jpg” } tells the photo album dialog to copy the image to system.TemporaryDirectory and name it after the user’s userName and make it a jpeg file. When this completes and if the user **did not** cancel the dialog (you need to handle that condition) then the photo will be in a place where your upload code will find it. But until you can tap your button and select and image and say to use it, you won’t be able to upload it.

I don’t know of a way to tell you how to determine if an image is a GIF, JPEG or PNG, so I’m going to say it’s limited to JPEG and PNG.

Rob

Hi, in case anyone happened upon this forum thread while trying to research uploading images, perhaps consider Develephant’s “Amazon S3 Lite” plugin in the Corona Marketplace. https://marketplace.coronalabs.com/plugin/s3-lite

I ran into complications with PUTs and POSTs of multipart form data too for image uploads,and this plugin solved my problem.  You’d have to learn a little about Amazon S3 and its permissions, but it’s worth it