Upload Image to MySQL BLOB using PHP PDO

An uploaded image is available temporarily through $_FILES. After validating the upload, open the temporary file in binary mode and bind it to a PDO placeholder using PDO::PARAM_LOB.

require 'config.php';

$photo=fopen(
    $_FILES['file_up']['tmp_name'],
    'rb'
);

$sql="INSERT INTO student_profile
      (id,student,profile_photo)
      VALUES
      (:id,:student,:profile_photo)";

$stmt=$dbo->prepare($sql);

$stmt->bindValue(
    ':id',
    $id,
    PDO::PARAM_INT
);

$stmt->bindValue(
    ':student',
    $student,
    PDO::PARAM_STR
);

$stmt->bindParam(
    ':profile_photo',
    $photo,
    PDO::PARAM_LOB
);

$stmt->execute();

The database operation uses a prepared INSERT statement. Before reaching this code, however, the uploaded file must be checked for upload errors, size and actual file type.

Upload image to MySQL BLOB using PHP PDO

User upload binary data or image to store in MySQL BLOB data type using PHP PDO

Sample MySQL BLOB Table Top ↑

The existing Plus2net example uses the student_profile table with these fields:

ColumnPurpose
idStudent ID
studentStudent name
profile_photoBinary image data

The MySQL BLOB data type stores binary data rather than ordinary text.

A MySQL BLOB column stores a maximum of 65,535 bytes. Many modern images are larger than this. The sample below therefore limits uploads to 60,000 bytes. For larger images, consider MEDIUMBLOB or storing the file outside the database.

Create the HTML File Upload Form Top ↑

A file upload form must use method="post" and enctype="multipart/form-data".

<form action="uploadck.php"
      method="post"
      enctype="multipart/form-data">

Name:
<input type="text" name="t1" maxlength="10" required>

Select one ID:
<select name="id" required>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>

Upload JPEG image:
<input type="file"
       name="file_up"
       accept="image/jpeg"
       required>

<input type="submit" value="Upload Image">
</form>

The browser-side accept attribute is only a convenience for the visitor. PHP must still validate the uploaded file on the server.

See the HTML file upload field tutorial for the form control itself.

Understanding the PHP $_FILES Array Top ↑

After a successful upload, PHP stores information about the file in:

$_FILES['file_up']

Important entries include:

EntryMeaning
nameOriginal filename supplied by the browser
typeClient-supplied MIME information; do not trust it for validation
tmp_nameTemporary server-side uploaded file
errorPHP upload status code
sizeUploaded size in bytes
Do not validate an upload using only $_FILES['file_up']['type'] or the filename extension. Both originate from client-side information.

Validate the Uploaded Image Top ↑

For this sample, we will accept only JPEG images up to 60,000 bytes so the file fits in the existing MySQL BLOB column.

if(
    !isset($_FILES['file_up']) ||
    $_FILES['file_up']['error']!==UPLOAD_ERR_OK
){
    exit('File upload failed.');
}

$max_bytes=60000;

if(
    $_FILES['file_up']['size']<=0 ||
    $_FILES['file_up']['size']>$max_bytes
){
    exit('Image must be smaller than 60 KB.');
}

$tmp_name=$_FILES['file_up']['tmp_name'];

if(!is_uploaded_file($tmp_name)){
    exit('Invalid uploaded file.');
}

$finfo=new finfo(
    FILEINFO_MIME_TYPE
);

$mime=$finfo->file($tmp_name);

if($mime!=='image/jpeg'){
    exit('Only JPEG images are allowed.');
}

$image_info=getimagesize($tmp_name);

if($image_info===false){
    exit('Uploaded file is not a valid image.');
}

This performs several different checks instead of relying on the filename alone.

Store the Uploaded Image with PDO Top ↑

Validate the ordinary form values before preparing the INSERT.

$id=filter_input(
    INPUT_POST,
    'id',
    FILTER_VALIDATE_INT,
    [
        'options' => [
            'min_range' => 1
        ]
    ]
);

$student=trim(
    $_POST['t1'] ?? ''
);

if($id===false || $id===null){
    exit('Invalid student ID.');
}

if(
    $student==='' ||
    strlen($student)>10
){
    exit('Student name is invalid.');
}

Then open the validated temporary image in binary mode:

$photo=fopen(
    $tmp_name,
    'rb'
);

if($photo===false){
    exit('Unable to read uploaded image.');
}

Bind the stream as PDO::PARAM_LOB:

$sql="INSERT INTO student_profile
      (id,student,profile_photo)
      VALUES
      (:id,:student,:profile_photo)";

$stmt=$dbo->prepare($sql);

$stmt->bindValue(
    ':id',
    $id,
    PDO::PARAM_INT
);

$stmt->bindValue(
    ':student',
    $student,
    PDO::PARAM_STR
);

$stmt->bindParam(
    ':profile_photo',
    $photo,
    PDO::PARAM_LOB
);

$stmt->execute();

Complete PHP Upload and PDO BLOB Example Top ↑

This combines the form-value checks, PHP upload checks, MIME validation and PDO INSERT.

<?php
require 'config.php';

if(
    $_SERVER['REQUEST_METHOD']!=='POST'
){
    exit('Invalid request.');
}

$id=filter_input(
    INPUT_POST,
    'id',
    FILTER_VALIDATE_INT,
    [
        'options' => [
            'min_range' => 1
        ]
    ]
);

$student=trim(
    $_POST['t1'] ?? ''
);

if($id===false || $id===null){
    exit('Invalid student ID.');
}

if(
    $student==='' ||
    strlen($student)>10
){
    exit('Student name is invalid.');
}

if(
    !isset($_FILES['file_up']) ||
    $_FILES['file_up']['error']!==UPLOAD_ERR_OK
){
    exit('File upload failed.');
}

$max_bytes=60000;

if(
    $_FILES['file_up']['size']<=0 ||
    $_FILES['file_up']['size']>$max_bytes
){
    exit('Image must be smaller than 60 KB.');
}

$tmp_name=$_FILES['file_up']['tmp_name'];

if(!is_uploaded_file($tmp_name)){
    exit('Invalid uploaded file.');
}

$finfo=new finfo(
    FILEINFO_MIME_TYPE
);

$mime=$finfo->file($tmp_name);

if($mime!=='image/jpeg'){
    exit('Only JPEG images are allowed.');
}

if(getimagesize($tmp_name)===false){
    exit('Uploaded file is not a valid image.');
}

$photo=null;

try{
    $photo=fopen(
        $tmp_name,
        'rb'
    );

    if($photo===false){
        throw new RuntimeException(
            'Unable to open uploaded image.'
        );
    }

    $sql="INSERT INTO student_profile
          (id,student,profile_photo)
          VALUES
          (:id,:student,:profile_photo)";

    $stmt=$dbo->prepare($sql);

    $stmt->bindValue(
        ':id',
        $id,
        PDO::PARAM_INT
    );

    $stmt->bindValue(
        ':student',
        $student,
        PDO::PARAM_STR
    );

    $stmt->bindParam(
        ':profile_photo',
        $photo,
        PDO::PARAM_LOB
    );

    $stmt->execute();

    echo 'Student data and image were added.';
}catch(Throwable $e){
    error_log(
        'Image upload error: '.
        $e->getMessage()
    );

    echo 'Unable to save the uploaded image.';
}finally{
    if(is_resource($photo)){
        fclose($photo);
    }
}
For a production form that changes user data, add authentication/authorization where required and CSRF protection. Prepared statements protect the SQL values; they do not replace those application security controls.

MySQL BLOB Size Matters Top ↑

MySQL provides several binary large-object types with different maximum sizes:

TypeApproximate maximum
TINYBLOB255 bytes
BLOB65,535 bytes
MEDIUMBLOB16 MB
LONGBLOB4 GB

A normal photograph can easily exceed the capacity of a basic BLOB column. If the application is intended to store larger profile images, use an appropriately sized column such as MEDIUMBLOB after considering database and application limits.

The server must also be able to accept and process the file size. Increasing the database column size alone does not change PHP's upload limits.

Supporting JPEG, PNG and Other Image Types Top ↑

The current sample table stores only the binary image. That works when the application already knows the content type, such as this JPEG-only example.

If different image formats are accepted, store the detected MIME type with the binary data:

CREATE TABLE student_profile_new (
    id INT PRIMARY KEY,
    student VARCHAR(50) NOT NULL,
    profile_photo MEDIUMBLOB NOT NULL,
    mime_type VARCHAR(50) NOT NULL
);

Then validate against a strict server-side allowlist:

$allowed_mimes=[
    'image/jpeg',
    'image/png',
    'image/webp'
];

if(
    !in_array(
        $mime,
        $allowed_mimes,
        true
    )
){
    exit('Unsupported image type.');
}

The saved MIME type can later be used when the BLOB is returned to the browser. We will cover that in PDO BLOB display and update.

PHP File Upload Error Codes Top ↑

Always check the upload error value before reading tmp_name.

ConstantMeaning
UPLOAD_ERR_OKUpload completed successfully
UPLOAD_ERR_INI_SIZEFile exceeded PHP's upload_max_filesize
UPLOAD_ERR_FORM_SIZEFile exceeded a form-defined limit
UPLOAD_ERR_PARTIALOnly part of the file was uploaded
UPLOAD_ERR_NO_FILENo file was uploaded
UPLOAD_ERR_NO_TMP_DIRTemporary upload directory is missing
UPLOAD_ERR_CANT_WRITEPHP could not write the temporary file
UPLOAD_ERR_EXTENSIONA PHP extension stopped the upload

PHP upload_max_filesize and post_max_size Top ↑

PHP configuration can reject an upload before the application code processes the file.

Common settings include:

upload_max_filesize = 2M
post_max_size = 8M

post_max_size must be large enough for the complete POST request, including the uploaded file and other form fields.

If a POST request exceeds post_max_size, PHP can provide an empty or incomplete $_POST/$_FILES request, so an application should not assume that a missing file always means the visitor forgot to choose one.

Should Images Be Stored in MySQL BLOB Columns? Top ↑

Storing images in the database can be appropriate when the binary data needs to remain closely associated with database records, database permissions or transactional workflows.

However, it is not the only design:

  • Database BLOB: binary data and record can be managed together.
  • File system/object storage: database stores a filename, key or URL while the binary file is stored separately.

For a small tutorial and compact binary data, BLOB storage is useful for learning PDO LOB handling. For large files or large-scale media delivery, storing the binary file outside the database is often more practical.

Common PDO File Upload Problems Top ↑

Reading tmp_name before Checking the Upload Error Top ↑

Check $_FILES['file_up']['error'] first. A temporary filename should not be assumed to represent a successful upload.

Trusting the Filename Extension Top ↑

A file named photo.jpg is not necessarily a JPEG. Detect the content type from the temporary file on the server.

Trusting $_FILES['type'] Top ↑

The browser supplies this value. Use server-side MIME detection such as finfo for validation.

Ignoring the BLOB Column Size Top ↑

A basic MySQL BLOB is limited to about 64 KB. Larger images require a larger binary column or a different file-storage strategy.

Forgetting enctype in the HTML Form Top ↑

File uploads require:

enctype="multipart/form-data"

Displaying Raw PDO Errors Top ↑

Log database errors on the server rather than printing errorInfo() or raw exception messages to visitors.

Assuming Prepared Statements Validate Files Top ↑

Prepared statements protect SQL parameter handling. They do not verify MIME type, image validity, upload size or application permissions.

Using a Transaction for One Independent Upload INSERT Top ↑

A single independent INSERT normally does not require an explicit PDO transaction. Transactions become useful when the upload must succeed together with other dependent database changes.

PDO DROP PDO BLOB: Add, Update & Display PDO INSERT

PDO Connection MySQL BLOB Data Type HTML File Upload Field

Download PDO BLOB Example Scripts

Frequently Asked Questions Top ↑

Q1: How do I store an uploaded image in MySQL using PDO?

Validate the PHP upload, open the temporary file in binary mode, prepare an INSERT statement and bind the file stream to a placeholder using PDO::PARAM_LOB.

Q2: Why must a PHP upload form use multipart/form-data?

multipart/form-data allows the browser to send binary file content as part of the POST request. Without it, the uploaded file will not be available normally through $_FILES.

Q3: Should I trust the uploaded file extension or $_FILES type?

No. Validate the temporary file on the server using MIME detection and, for images, an image-validation function such as getimagesize().

Q4: How large a file can a MySQL BLOB store?

A MySQL BLOB stores up to 65,535 bytes. Larger binary data may require MEDIUMBLOB, LONGBLOB or an external file-storage approach.

Q5: Why use PDO::PARAM_LOB?

PDO::PARAM_LOB tells PDO that the bound value represents large-object or binary data. A file stream can be bound to the BLOB placeholder using this parameter type.

Q6: Should I store the MIME type in the database?

If the application accepts different binary or image formats, storing the server-detected MIME type allows the correct Content-Type to be sent when the BLOB is retrieved.

Q7: Is storing an image in a database always better than storing a file path?

No. BLOB storage can be useful when binary data belongs closely with database records, while file-system or object storage is often more practical for large files and high-volume media delivery.



PDO DROP PDO BLOB PDO References


Subscribe to our YouTube Channel here



plus2net.com











PHP video Tutorials
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
©2000-2026   plus2net.com   All rights reserved worldwide Privacy Policy Disclaimer