Image from database is not being displayed!?

Sorry about that. After messing around a little bit I had to put the file upload query and all of the code regarding the $_FILES on top of all of the other querys. For some reason have two querys trying to INSERT INTO users made it so it was creating an extra column. With the code I got it so now it inserts the $file_name into the avatar varchar in the database. I have updated the code on the pastebin here.

Now I onto getting that image to echo and display on the webpage desired correctly :smile:

lets have a look at what i previously posted

on an insert statement you first have to insert the user credentials to get a valid ID from auto_increment. after that use ::lastinsertid to get the ID and update the avatar afterwards with update

http://php.net/manual/en/pdo.lastinsertid.php

so: you do not insert an avatar, you just update the user record.

1 Like

After long journey through a lot of your much appreciated help I finally got it so it is:

  • Moving the avatar user uploaded from form to $upload_directory
  • Storing that $file_name into the database avatar column
  • Displaying the avatar on the hompeage of the user that is logged in :slight_smile:

Here is how I move the avatar into the $upload_directory:

if (in_array($image_extension, $valid_image_extensions)) {
  // move the file to desired directory ($upload_directory)
  move_uploaded_file($image_tmp_name, "$upload_directory/$file_name");
}

Here is how I store that $file_name in the database’s avatar column:

$query = dbConnect()->prepare("INSERT INTO users (username, password, email, activated, activation_id, avatar) VALUES (:username, :password, :email, :activated, :activation_id, :avatar)");
$query->bindParam(':avatar:', $file_name);

Here is how I display that users avatar:

// run query to get users avatar
$getAvatar = dbConnect()->prepare("SELECT * FROM users WHERE username = :username");
$getAvatar->bindParam(':username', $username);
// execute query
$getAvatar->execute();
// set each column as row
$row = $getAvatar->fetch(PDO::FETCH_ASSOC);
// set $avatar as users set avatar
$avatar = $row['avatar'];

if (empty($avatar)) {

  // echo default avatar...
  echo '<img src="http://i.imgur.com/QFxs0nX.png" class="user_avatar" alt="default avatar" />';

// if user has set an avatar:
} else {

  // echo set avatar...
  echo '<img src="includes/uploads/avatars/' .$avatar. '" class="user_avatar" alt="' .$username. ' avatar" />';

}

Only thing left to do as far as the avatar is making sure that the image is less then X size before moved into the $upload_directory which I am sure I shouldn’t have any issues regarding that as its a quick if statement where we move_uploaded_file.

Thanks again,
Codi :slight_smile:

good for you that this information already exists in your $_FILES!

1 Like

I’ve almost got it down but what would be a good size to set as maximum size? I uploaded a random image that I would use as an avatar and 'size' => int 61384. I am not sure what size measurement is being used here or what is a good integer to set as the maximum allowed.

Bytes, so the example you give is around 61k.

As for the limit, that’s up to you depending on how many users you’ll have, what your server spec is (in terms of storage space and bandwidth for displaying them). If it’s an avatar, you could look at a few forum sites and see what they use as recommended maximum dimensions, and go with something similar. Only you know where it will appear on screen, so what’s a reasonable size picture to have in that location?

1 Like

i don’t get why you still have so much trouble reading the manual:

The size, in bytes, of the uploaded file.

2 Likes

You seem to be struggling again. Here, I’ll just give you this because you seem to understand a little bit about PHP and at least you tried. That’s what I like.

<?php
// Generate Random String function taken from
// http://stackoverflow.com/questions/4356289/php-random-string-generator
function generateRandomString($length) {

    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;

}

$username = 'spaceshiptrooper'; // Assuming this is the username

// Check to see if the form was submitted
if($_SERVER['REQUEST_METHOD'] == 'POST') {

    $upload_directory = 'includes/uploads/avatar'; // The directory you want to store the avatars in.

    $image = $_FILES['avatar']; // Create and append a new variable to $_FILES['avatar'] so that we don't have to manually type in the full variable for it.

    $image_tmp_name = $image['tmp_name']; // Get the tmp file

    $image = getimagesize($image['tmp_name']); // Get the image size of the tmp file

    $image_mime = $image['mime']; // Create and append the mime array to $image_mime

    // List the mime types you want to allow in your upload system.
    $valid_image_mime = array('image/jpeg', 'image/jpg', 'image/png', 'image/gif');

    $random = generateRandomString(16); // Generate the set of characters

    $ext = '.jpg'; // The file extension we want it to end up being once it's uploaded.

    $file_name = $random . $ext; // Append the generated string along with the desired extension to the new file name.

    // Check to see if the mime type from the tmp file is in our array above.
    if(in_array($image_mime, $valid_image_mime)) {

        // So we are good to go. The mime type is listed in our array.

        // Check to see moving the tmp file to our desired directory works.
        if(move_uploaded_file($image_tmp_name, $upload_directory . '/' . $file_name)) {

            // Since the file has been uploaded, we can now update our old avatar records
            // And set it to the current one.

            $sql = "UPDATE users SET avatar = :avatar WHERE username = :username";
            $prepare = dbConnect()->prepare($sql);
            $parameters = array(':avatar' => $file_name, ':username' => $username);
            $prepare->execute($parameters);

            // Check to see if the query executed.
            if($prepare->rowCount()) {

                // print('Good');
                header('Location: ' . $_SERVER['REQUEST_URI']);

            } else {

                // print('Bad upload');
                header('Location: ' . $_SERVER['REQUEST_URI']);

            }

        } else {

            // print('Bad');
            header('Location: ' . $_SERVER['REQUEST_URI']);

        }

    } else {

        // print('Bad');
        header('Location: ' . $_SERVER['REQUEST_URI']);

    }

} else {

    // run query to get users avatar - Only select the columns you need.
    $sql = "SELECT avatar FROM users WHERE username = :username";
    $getAvatar = dbConnect()->prepare($sql);
    $parameters = array(':username' => $username);
    // execute query
    $getAvatar->execute($parameters);

    // Check to see if rowCount returned any data
    if($getAvatar->rowCount()) {

        // Loop the data in a while loop and create/ append the variable $row to the while loop.
        while($row = $getAvatar->fetch(PDO::FETCH_ASSOC)) {

            // set $avatar as users set avatar
            $avatar = $row['avatar']; // This data should be escaped.

            if(empty($avatar)) {

                // echo default avatar...
                echo '<img src="http://i.imgur.com/QFxs0nX.png" class="user_avatar" alt="default avatar" />';

            // if user has set an avatar:
            } else {

                // echo set avatar...
                echo '<img src="includes/uploads/avatar/' .$avatar. '" class="user_avatar" alt="' .$username. ' avatar" />';

            }

        }

    } else {

        // The username doesn't exist. Either the user is not who they are.
        // Or the user doesn't actually exist.
        print('Something is wrong. The username does not exist.');

    }
?>
<form action="" method="POST" enctype="multipart/form-data">
<input type="file" name="avatar">
<button type="submit">Submit</button>
</form>
<?php
}

So I’ll explain what we are doing here so you can understand what is needed and why that is.


So most of the explanation is already in the comments. To further your understanding. What we do first is, we check to see if the form was submitted. Most amateurs use if(isset($_POST)) or if(isset($_POST['submit'])) or if(isset($_POST['btn'])) or anything along those lines of if(isset) and that is the wrong way of doing it. This is an amateur hack back from the 90’s. The actual way of checking for form submission is if($_SERVER['REQUEST_METHOD'] == 'POST').

Anyways, moving on.

We basically create the variable $image and reference it whenever we want to use $_FILES['avatar'] instead of typing the full $_FILES['avatar']. First couple of references are tmp_name and mime. These two, we use them for the sole purpose of finding out the contents within the file. So if say we want to limit the file height to 500px. You can limit it by doing

if($image[1] > 500) {

    print('The file height is larger than 500px. Please resize it and re-upload it.');

}

So the reason why we are using mime is so that people can’t upload files that aren’t what they say they are. Simply by checking for “file extensions” are flawed. I strongly suggest you DO NOT do this. Rather, you should be checking for the mime content type. When a file is created by anything, it leaves a finger print which is mime type or mime content type. Basically, say I opened up a NotePad and I write a bunch of gibberish in there and save the file as .txt. When you upload it and check it using getimagesize(), the mime type returned is text/plain. Say if someone were to change that .txt file to .jpg. The whole “check for file extensions” method will be flawed because the ACTUAL file was not created using an image editor or image creator. When you upload it and check the new file using getimagesize(), the mime type will still return text/plain. Real genuine images have a mime type header of either image/jpg, image/jpeg, image/png, or image/gif. There are a few more, but I don’t recall them. Basically, checking for file extensions can be abused and rather not trust worthy.

Next, we generate a 16 random string using the function found on StackOverflow and append a .jpg or whatever you want as the file extension. Reason why we add our own file extension to this newly generated file name is because when you generate the file name. You still need to have the file extension in there in order for it to be output as an image. So we use .jpg since it’s a generic file extension that most people use.

After that, we check to see if the mime type from the uploaded file is in our list of array. If it is not, redirect them back to the upload page and here, you can give them a custom error using $_SESSION if you want. We basically halt and redirect them because again, the mime type is not an actual image and this can lead to a lot of security vulnerabilities. Next, if the file is indeed a genuine image, we then wrap it around another if statement to make sure that the file does indeed get uploaded. If it doesn’t get uploaded and it says “Success”, then we have a problem. So it’s a good thing to always check your work. I call this “Properly checking for errors”. Simply put, it’s a way to make sure that everything goes well. So say for example, you grab a user’s data from the database using a session cookie that sets their username to something. What if later, they delete their account and their information doesn’t exist on your website anymore? When this happens, if another user requests to look at that old user’s profile, you will end up with a blank page. This is why you should ALWAYS check your work. If you don’t put in the proper error handling, you basically will end up with a blank page. Basically, most of the time, if the data does not exist in the database and you don’t do proper error handling, you basically are giving your users a blank page. Here’s an example of how to do it wrong.

$id = $_GET['id'];

$sql = "SELECT name, location FROM users WHERE id = :id LIMIT 1";
$prepare = $db->prepare($sql);
$parameters = [':id' => $id];
$prepare->execute($parameters);

$row = $prepare->fetch(PDO::FETCH_ASSOC);

print($row['name']);

So in the above snippet, we are basically grabbing an ID from the URL and doing a query to grab the data using that ID. Let’s say we only have about 12 rows of users. So basically saying that you ONLY have 12 users currently using your service. Let’s just put in the ID # 1.

So that goes through and prints out the current name for user #1 since it’s within the 12 users we have. Now let’s say someone requests for ID #13. That ID doesn’t exist in our database since our current user base is only set at 12 right now. Ohhhhhhhh oooopppppsssss. No data is output and nothing is output onto the screen, not even a “Sorry, you’ve reached the end of our results.” warning. Just a blank empty page. Nothing for the user to see. Nothing to tell the user anything. This is why “Properly checking for errors” is important. If the output is supposed to be say an image of the requested user. It would be something like this.

$id = $_GET['id'];

$sql = "SELECT name, location, avatar FROM users WHERE id = :id LIMIT 1";
$prepare = $db->prepare($sql);
$parameters = [':id' => $id];
$prepare->execute($parameters);

$row = $prepare->fetch(PDO::FETCH_ASSOC);

print('<img src="http://example.com/uploads/avatar/' . $row['avatar'] . '>');

What really gets output when you don’t do “Proper error handling” is this.

<img src="http://example.com/uploads/avatar/">

Is that what you really want your users to see? So simple isn’t always the best in every case.


Any who. Let’s get back to the topic. Now, the next thing we do is we basically update the old avatar to the new avatar. And once the user gets redirected to the upload page. They will see the new avatar instead of the old one.


The rest is basically the same thing, except for outputting data instead of updating data.

2 Likes

First off, thank you for this! I really appreciate you coming here and explaining everything in detail for me. One issue I am running into though is actually displaying the avatar WHERE username = :username at the very end there. The form seems to be working great as it is inserting it into the database and the directory desired great!

The reason this issue is occuring though is I am using a complete other function ‘checkLogin()’ (that is not in the register.php page) to display the avatar in my navigation bar so I tried to cut that last } else { statement out and insert it there instead of using the if(isset($_SESSION['username'])) { statement but for some reason I am getting an error like this on the homepage where I am trying to display the avatar:
https://puu.sh/ssMDR/9af663c3af.png

Line 46 would be where I set the $username variable in this function:
$username = $_SESSION['username']

I get this error when being logged out and the checkLogin() function being included but not sure how exactly I should be fixing this. Also when just moving directorys manually by changing url I log in and also, it doesn’t display the avatar at all. Instead display some sort of broken image as it looks like here:
https://puu.sh/ssMLT/6acea41a73.png

What is working:

  • Images that users upload are being put in the directory(includes/uploads/avatars) great!

What is not working:

  • The avatar’s $file_name in register.php is not being inserted into the database. Database shows: ‘NULL’ in the database row: avatar.
  • The user receives error: undefined index: username in A:\wamp64\heartfx\core\common.inc.php on line 46.
  • When user is logged in, the avatar is not displayed. Instead shows some sort of broken image.

Some of my source code to reference to:

@spaceshiptrooper’s i see several flaws in your code and concept:

  1. There’s absolutely nothing wrong with checking the button. Its not ‘oldschool’ nor ‘amateur’ - it is just right to evaluate what ever information somebody requires by the user - if that’s the button, you should evaluate the button. If somebody ever wants to know or requires the request_method - evaluate the request_method, if you are sure you want to react on empty POST-requests (which will likely never happen by accident if you requested certain information via a form you send to the user first). But thats at least a totally different task. And this does not even consider which button somebody choose. And for the example code: you think checking the request_method is good - but you are not even checking if $_FILES[avatar] exists? what’s that?

  2. This still hardly fails on duplicate random strings. They “maybe” never occur - but first, randomness is not made for that, bute uniqueness is, and second, why would you build a system to fail? Especially if its so easy to prevent your system to fail by using uniqeid (not perfect, but better then guessing), record-id, or even getting new random strings until a not already existing filename is found.

  3. I do not see why someone would change the file extension - i already posted a whitelist which includes a direct translation from MIME types to extensions and a sample code that gets the appropriate extension. That also prevents any extension-based webserver (tested with PHPs build in webserver) to populate the wrong MIME type via header. (“oh i saved this image but my irfanview reported that the extension is wrong, why would that dick website send me wrong files?” - real life example)

  4. also keep in mind that getimagesize() only checks a few bytes in the header to determine the filetype, so it’s only a slight securit enhancement

1 Like

Without wanting to drag the thread off-topic, and taking into account the comment from @chorn later on, this is something I’ve seen on here and had a question about. Which is: If I wanted my php script to deal with form submissions from multiple different forms, purely for the sake of having all the code in the same script, how would I determine which button was pressed without using the old-style amateur hack as you put it? Would I simply stick a hidden variable in the form, or add the check for which button inside the check for request-method?

Before you access a session variable, you should check that it exists. If your checkLogin() function is being called when you are logged out, it seems clear that $_SESSION['username'] will not exist, and therefore you’ll get an undefined index.

The check is

if (isset($_SESSION['username'])) { 
  $username = $_SESSION['username'];
  ..

I would then put the closing } after all the code to access the database. I was going to say where your function deals with an invalid username, but it just seems to silently exit. That might be fine for the site, in which case just put the closing } where you have all the others.

It seems to me that your code runs through various checks for duplicate names, invalid image types, and so on, but it seems that it moves the uploaded file, attempts to set the value of the avatar column in the users table for the username (except you haven’t created the user row at that point), then redirects to the same place whether it worked or not. After that (i.e. never, because of the header redirect) it then tries to create the user record, which of course will have a blank avatar.

Reading again, I think it won’t do the header redirect because there’s already stuff been sent to the browser before that point, so the issue is that you attempt to insert the avatar filename into the user row before you’ve created the user row.

1 Like

I got a lot of errors trying the new method that @spaceshiptrooper (maybe I was missing something from the code and explanation he provided) but the site I am creating right now is for a small community to say the least and think the original method that @chorn and I resolved through all 60+ replies :grimacing: will be the one that I use. Everything works without any errors and is a great method to use on my site where it will soon be live on. I do appreciate your advice through the other error(s) though!

@chorn


  1. Obviously you think that all my snippets are complete simply by looking at them. I don’t finish them because I worked with what was given to me. I would have done exactly what I would do with my all real scripts and took the extra measure.

  2. Ah, just looked at uniqid and thank you so much for pointing that out because upon just loading up the page, this was given in the big red box.

Warning
This function tries to create unique identifier, but it does not guarantee 100% uniqueness of return value.

How trust worthy is this? Because it says on the actual doc that it DOES NOT guarantee uniqueness. Also, I assume that this function still generates using a random string in the milliseconds because how would it get the results it gets? The output would still be a random string regardless of how “unique” it is. So what was that?

3. Then you must be living in a fairy-tale world because the last time I checked in the human world, not everyone online is nice. There are actual hackers on and off he Internet and what makes you think they won’t target your website for their testing games? Also, there are tons of script kiddies who like to test to see what makes your website tick. So failing to see the point where someone would submit a random file that had their file extensions changed would because mean you are neglecting the fact that not everyone on the internet is going to be nice to you.

4. I would rather pick mime_content_type since it was what I used when I had learned about mime types. But I don’t know if @cscodismith is using PHP 7 or not. It was removed some where in PHP 5, but was re-introduced back in PHP 7.


@droopsnoot


Well, why wouldn’t you be targeting a different file for that matter? I honestly don’t think stuffing everything in the same file is something one should be doing. Here’s something I found with just a quick search on Google for php spaghetti code. http://stackoverflow.com/a/2573714

Read the first bullet and then ask yourself why one shouldn’t be stuffing everything in the same file.

Here’s an example of a perfectly good way of determining multiple forms in one file.

<form action="register.php" method="POST">
.....
</form>

<form action="login.php" method="POST">
.....
</form>

<form action="forgotpassword.php" method="POST">
.....
</form>

@cscodismith


And what were the errors saying? I am using PHP 7.1 RC5 and I am not getting any errors at all. My error logs are on and I can show you if you want using a .gif image.

That’s the way I used to do it, I can’t exactly remember what made me switch to bringing it into a single file.

Probably something about time saving? It’s easier to do everything all in one file, but when your file gets too big, the execution time is much slower. So it’s a lot better if one separates form submissions based on the submission type (eg: Login, Sign Up, Forgot A Password).


#Off-topic below


I must admit that my model file is starting to become a mess like this. I found out a way to keep everything separated and have it still working, but with an already almost set to go application, it’s already going to be time consuming to convert everything to the new method since I have 630 files all using the same model file. So that’s the only reason why I still stuff all the data crap in the same model file. Though I am starting to get a little edgy on just screwing everything and separating all the database calls and creating a sub-class file for each related topic. Such as put everything that relates to user account in one sub-class file. And everything that relates to the admin panel in one sub-class file. This in turn would separate and give myself more room to add in more data. My model file for PDO is already at 8055 lines. For my MySQLi file, it’s at 9424 lines. So I would say that having such a big file would basically be an annoyance to the next developer who would have to manage my application. That’s why I say to separate each file by its logic or name. So instead of stuffing everything in one file that has no relations to Login. I would say separate them.

  1. yes, of course - if you are posting examples and complaining so much about other developers skills, trying to degrade their methods, without considering their requirements, while not telling about security issues and silly condition checking, while posting sourcode to beginners, that not only is not helping, but also totally flaws general and your own security advices - thats at least questionable.

  2. oh i see you did not read the manual properly, otherwise you would have seen that uniqid depends on microseconds, so you can generate at least 1k unique strings per second (not repeating as the manual also states along with creating more entropy, prefixing etc.). looks like you do not even get the difference between random and unique. and nice that you easily ignore every other solution i mentioned that prevent doubles while your guessing does not. i still don’t see why to leave an application in such an unstable state, while there are 10 LOC solutions.

  3. makes no sense. what does a fake extension, for which there is no technical reason, for which against is a whitelist, has to do with scriptkiddies? nothing.

  1. Complaining? Who’s complaining here? I’m just stating that amateurs tend to use legacy codes and most amateurs who use these kinds of legacy codes get very touchy when their nerve is struck when someone says “Don’t use these codes, it’s from an amateur.” Also,

Oh really? So posting code, explaining about what happens in the codes is what you call “Not helping”? So what makes your 60+ posts in this topic mean then? Look, I know probably have some sort of disagreement with me probably from what I have said in the past. But “complaining” about it to me won’t “help” in that matter at all. Also, “complaining so much about other developers skills” isn’t this what you are doing right now? So who really is complaining here?

Let’s just all calm down because I have no clue why you are so ticked off about when I am suggesting that @cscodismith could use a little more work since he still doesn’t seem to have everything working after

60 + posts into this thread


2. I am not ignoring anything. I do see that on the docs. But what is most striking to me is that it WARNS you about what it’s doing and could potentially NOT do and you ignore that fact and say that it WILL ALWAYS do what you want it to do.

3. Right again, you’re just going to ignore the fact that not everyone is nice on the internet. I guess you do live in a fairy-tale world.


But I’m not going to argue anymore with someone who seems to ignore everything that is factual and thinks that everything is going to be alright when there are so many things that are wrong with the current snippet that @cscodismith has posted.

I have everything working on my end with just what I have posted and I am getting tremendous results while the OP is still having issues with your help after 60 + posts. And the reason why @cscodismith is having problems with mine is most likely because they are trying to modify mine to fit their needs. So whatever I posted works perfectly fine as is. I am not at fault for anything that is modified after what I posted. I’ll just sit here and see how far this goes until you get everything working or if this topic gets abandon because nothing seems to work after 100 + posts.

for 1. you stated one special code to be generally legacy, which is still neither legacy nor only used by amateurs when it comes to real-life requirements. so in which situation am i more interested in which request_mothod the user gave me, than in what actual data the user provided, or what action he wants to perform? thats what a button does, that’s what you call “90s”. and i am still not complaining about skills, or developers, i’m criticising that there is still code that not even follows the developers guidelines for validating everything (which itself is right! but it’s not shown…). as you see, beginners tend to copy-and-paste code while they are learning, that leads to security issues when not discussed in the first place.

i’m not willing to tell everything i say is right, or even complete, so don’t mind do cite me:

uniqeid (not perfect, but better then guessing)

i’m comparing two methods, still one question is open: why is generating pseudo-random strings over and over without checking on existing files better then using a sequence?

while the OP is still having issues

oh thats not what OP said:

Everything works without any errors

in total your script just opens new problems i discussed, that’s why i call it “not helping” - it still misses proper validation and i still see a new thread coming up:

  • “why was an avatar of another user overwritten”

in short 3. is only what i said at the beginning:

your resulting filenamem ay look like $userid.$validextension

no check for duplicate strings, no need for a fake-extension, i already provided a working example. i still don’t get what your problem with this is, just show me what’s wrong with the code from #52 and where you see security issues leading to that “farytail” i’m living in. what attack do you think of?

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.