ESP32-CAM Post Images to Local or Cloud Server using PHP (Photo Manager)

Learn how to make HTTP POST requests using the ESP32-CAM board with Arduino IDE to send photos to a server. We’ll show how to post a JPG/JPEG image to a local server (Raspberry Pi LAMP server) or to a cloud server (that you can access from anywhere). The photos will be displayed in a gallery where you can view or delete the photos. To save the images in the server and create the gallery, we’ll use PHP scripts.

ESP32-CAM Camera Board Send Post Images to Local Cloud Server HTTP POST Arduino IDE

To build this project, you need to follow the next steps. Follow the LAMP Server or the Hosting Server instructions depending if you want to access the photos locally or from anywhere.

  1. Hosting your PHP Application
    1. Raspberry Pi LAMP Server (local access)
    2. Hosting Server (access from anywhere)
  2. PHP scripts to save and display photos in the server
    1. Raspberry Pi LAMP Server (local access)
    2. Hosting Server (access from anywhere)
  3. Program the ESP32-CAM with Arduino IDE
  4. Testing and Final Demonstration

1. Hosting Your PHP Application

The goal of this project is to have a local or cloud server to store and access your ESP32-CAM photos.

1. Raspberry Pi local server:

With a Raspberry Pi LAMP server, you can access your images locally (as illustrated below).

ESP32-CAM Camera Board Send Photo Image to Raspberry Pi LAMP Server PHP gallery Script Arduino

Setup Local RPi LAMP Server »

2. Cloud server (Bluehost hosting solution)

You also can visualize the ESP32-CAM photos from anywhere in the world by accessing your own server + domain. Here’s a high level overview on how it works:

ESP32-CAM Camera Board Send Photo Image to Cloud Server PHP gallery Script Arduino
  • Bluehost (user-friendly with cPanel): free domain name when you sign up for the 3-year plan. I recommend choosing the unlimited websites option; Note that any hosting service that offers PHP will work with this tutorial. If you don’t have a hosting account, I recommend signing up for Bluehost.

Get Hosting and Domain Name with Bluehost »

When buying a hosting account, you’ll also have to purchase a domain name. This is what makes this project interesting: you’ll be able to go your domain name (http://example.com) and see your ESP32-CAM photos. If you like our projects, you might consider signing up to Bluehost, because you’ll be supporting our work.

HTTP POST Request Method

The Hypertext Transfer Protocol (HTTP) works as a request-response protocol between a client and server. Here’s an example:

  • The ESP32 (client) submits an HTTP request to a Server (for example: local RPi Lamp Server or example.com);
  • The server returns a response to the ESP32 (client);

HTTP POST is used to send data to a server to create/update a resource. For example, publish an image to a server.

POST /upload.php HTTP/1.1
Host: example.com
Content-Type: image/jpeg

2.1. Preparing Your .php Files and uploads Folder (Raspberry Pi LAMP Server)

This section prepares your .php files and uploads folder for your Raspberry Pi LAMP Server. If you’re using your own server + domain name, skip to the next section.

Having a Raspberry Pi running Apache and PHP, in the Raspberry Pi board terminal window navigate to the /var/www/html/ directory:

[email protected]:~ $ cd /var/www/html/

Create a new folder called uploads:

[email protected]:/var/www/html $ mkdir uploads
[email protected]:/var/www/html $ ls
uploads

At the moment, /var/www/html is owned by root, use the next commands to change to the pi user and give it all permissions so that you can save photos using a PHP script later on.

sudo chown -R pi:pi /var/www/html
chmod -R 777 /var/www/html/

Finally, create a new upload.php file:

[email protected]:/var/www/html $ nano upload.php

This PHP script is responsible for receiving incoming images from the ESP32-CAM, rename the images with a timestamp and store them in the uploads folder. Edit the newly created file (upload.php) and copy the following snippet:

<?php
// Rui Santos
// Complete project details at https://RandomNerdTutorials.com/esp32-cam-post-image-photo-server/
// Code Based on this example: w3schools.com/php/php_file_upload.asp

$target_dir = "uploads/";
$datum = mktime(date('H')+0, date('i'), date('s'), date('m'), date('d'), date('y'));
$target_file = $target_dir . date('Y.m.d_H:i:s_', $datum) . basename($_FILES["imageFile"]["name"]);
$uploadOk = 1;
$imageFileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));

// Check if image file is a actual image or fake image
if(isset($_POST["submit"])) {
  $check = getimagesize($_FILES["imageFile"]["tmp_name"]);
  if($check !== false) {
    echo "File is an image - " . $check["mime"] . ".";
    $uploadOk = 1;
  }
  else {
    echo "File is not an image.";
    $uploadOk = 0;
  }
}

// Check if file already exists
if (file_exists($target_file)) {
  echo "Sorry, file already exists.";
  $uploadOk = 0;
}

// Check file size
if ($_FILES["imageFile"]["size"] > 500000) {
  echo "Sorry, your file is too large.";
  $uploadOk = 0;
}

// Allow certain file formats
if($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg"
&& $imageFileType != "gif" ) {
  echo "Sorry, only JPG, JPEG, PNG & GIF files are allowed.";
  $uploadOk = 0;
}

// Check if $uploadOk is set to 0 by an error
if ($uploadOk == 0) {
  echo "Sorry, your file was not uploaded.";
// if everything is ok, try to upload file
}
else {
  if (move_uploaded_file($_FILES["imageFile"]["tmp_name"], $target_file)) {
    echo "The file ". basename( $_FILES["imageFile"]["name"]). " has been uploaded.";
  }
  else {
    echo "Sorry, there was an error uploading your file.";
  }
}
?>

View raw code

Your upload.php file should look like this. Save your file and exit (Ctrl+X, Y, and Enter key):

Upload images to server local or cloud using the ESP32-CAM board Arduino IDE upload.php file

Then, create a new gallery.php file:

[email protected]:/var/www/html $ nano gallery.php

Edit the newly created file (gallery.php) and copy the following snippet:

<!-- 
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-cam-post-image-photo-server/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
-->
<!DOCTYPE html>
<html>
<head>
  <title>ESP32-CAM Photo Gallery</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    .flex-container {
      display: flex;
      flex-wrap: wrap;
    }
    .flex-container > div {
      text-align: center;
      margin: 10px;
    }
  </style>
</head><body>
<h2>ESP32-CAM Photo Gallery</h2>
<?php
  // Image extensions
  $image_extensions = array("png","jpg","jpeg","gif");

  // Check delete HTTP GET request - remove images
  if(isset($_GET["delete"])){
    $imageFileType = strtolower(pathinfo($_GET["delete"],PATHINFO_EXTENSION));
    if (file_exists($_GET["delete"]) && ($imageFileType == "jpg" ||  $imageFileType == "png" ||  $imageFileType == "jpeg") ) {
      echo "File found and deleted: " .  $_GET["delete"];
      unlink($_GET["delete"]);
    }
    else {
      echo 'File not found - <a href="gallery.php">refresh</a>';
    }
  }
  // Target directory
  $dir = 'uploads/';
  if (is_dir($dir)){
    echo '<div class="flex-container">';
    $count = 1;
    $files = scandir($dir);
    rsort($files);
    foreach ($files as $file) {
      if ($file != '.' && $file != '..') {?>
        <div>
          <p><a href="gallery.php?delete=<?php echo $dir . $file; ?>">Delete file</a> - <?php echo $file; ?></p>
          <a href="<?php echo $dir . $file; ?>">
            <img src="<?php echo $dir . $file; ?>" style="width: 350px;" alt="" title=""/>
          </a>
       </div>
<?php
       $count++;
      }
    }
  }
  if($count==1) { echo "<p>No images found</p>"; } 
?>
  </div>
</body>
</html>

View raw code

This PHP script is responsible for displaying the images on the gallery. Your gallery.php file should look like this. Save your file and exit (Ctrl+X, Y, and Enter key):

gallery PHP script save and view images server ESP32-CAM

2.2. Preparing Your .php Files and uploads Folder (Hosting Service)

If you prefer to run your server remotely and access the photos from anywhere, you need a hosting account. After signing up for a hosting account and setting up a domain name, you can login to your cPanel or similar dashboard. After that, open the File Manager.

Open the “Advanced” tab and select “File Manager“:

Bluehost open advanced and file manager files to create upload.php file and uploads folder

Then, select the public_html option. Press the “+ File” button to create a new upload.php file and a new gallery.php file. Then, click the “+Folder” button to create the Uploads folder.

ESP32-CAM Camera board CPanel Create New Upload PHP File Uploads Folder

With the three items created, edit the upload.php file:

ESP32-CAM Camera board CPanel Create New PHP Files and Uploads Folder

This PHP script is responsible for receiving incoming images from the ESP32-CAM, rename the images with a timestamp and store them in the uploads folder. Edit the newly created file (upload.php) and copy the following snippet:

<?php
// Rui Santos
// Complete project details at https://RandomNerdTutorials.com/esp32-cam-post-image-photo-server/
// Code Based on this example: w3schools.com/php/php_file_upload.asp

$target_dir = "uploads/";
$datum = mktime(date('H')+0, date('i'), date('s'), date('m'), date('d'), date('y'));
$target_file = $target_dir . date('Y.m.d_H:i:s_', $datum) . basename($_FILES["imageFile"]["name"]);
$uploadOk = 1;
$imageFileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));

// Check if image file is a actual image or fake image
if(isset($_POST["submit"])) {
  $check = getimagesize($_FILES["imageFile"]["tmp_name"]);
  if($check !== false) {
    echo "File is an image - " . $check["mime"] . ".";
    $uploadOk = 1;
  }
  else {
    echo "File is not an image.";
    $uploadOk = 0;
  }
}

// Check if file already exists
if (file_exists($target_file)) {
  echo "Sorry, file already exists.";
  $uploadOk = 0;
}

// Check file size
if ($_FILES["imageFile"]["size"] > 500000) {
  echo "Sorry, your file is too large.";
  $uploadOk = 0;
}

// Allow certain file formats
if($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg"
&& $imageFileType != "gif" ) {
  echo "Sorry, only JPG, JPEG, PNG & GIF files are allowed.";
  $uploadOk = 0;
}

// Check if $uploadOk is set to 0 by an error
if ($uploadOk == 0) {
  echo "Sorry, your file was not uploaded.";
// if everything is ok, try to upload file
}
else {
  if (move_uploaded_file($_FILES["imageFile"]["tmp_name"], $target_file)) {
    echo "The file ". basename( $_FILES["imageFile"]["name"]). " has been uploaded.";
  }
  else {
    echo "Sorry, there was an error uploading your file.";
  }
}
?>

View raw code

Save your file and exit.

Then, edit the gallery.php file and copy the following snippet. This is responsible for displaying the images in the gallery.

<!-- 
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-cam-post-image-photo-server/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
-->
<!DOCTYPE html>
<html>
<head>
  <title>ESP32-CAM Photo Gallery</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    .flex-container {
      display: flex;
      flex-wrap: wrap;
    }
    .flex-container > div {
      text-align: center;
      margin: 10px;
    }
  </style>
</head><body>
<h2>ESP32-CAM Photo Gallery</h2>
<?php
  // Image extensions
  $image_extensions = array("png","jpg","jpeg","gif");

  // Check delete HTTP GET request - remove images
  if(isset($_GET["delete"])){
    $imageFileType = strtolower(pathinfo($_GET["delete"],PATHINFO_EXTENSION));
    if (file_exists($_GET["delete"]) && ($imageFileType == "jpg" ||  $imageFileType == "png" ||  $imageFileType == "jpeg") ) {
      echo "File found and deleted: " .  $_GET["delete"];
      unlink($_GET["delete"]);
    }
    else {
      echo 'File not found - <a href="gallery.php">refresh</a>';
    }
  }
  // Target directory
  $dir = 'uploads/';
  if (is_dir($dir)){
    echo '<div class="flex-container">';
    $count = 1;
    $files = scandir($dir);
    rsort($files);
    foreach ($files as $file) {
      if ($file != '.' && $file != '..') {?>
        <div>
          <p><a href="gallery.php?delete=<?php echo $dir . $file; ?>">Delete file</a> - <?php echo $file; ?></p>
          <a href="<?php echo $dir . $file; ?>">
            <img src="<?php echo $dir . $file; ?>" style="width: 350px;" alt="" title=""/>
          </a>
       </div>
<?php
       $count++;
      }
    }
  }
  if($count==1) { echo "<p>No images found</p>"; } 
?>
  </div>
</body>
</html>

View raw code

Save your file and exit. That’s it! Your server is ready.

3. ESP32-CAM HTTP Post Images/Photos to Server

Now that you have your server ready (Raspberry Pi LAMP server or cloud server), it’s time to prepare the ESP32-CAM with the code to publish a new image to your server every 30 seconds. Before proceeding with this tutorial, make sure you complete the following prerequisites.

Parts Required

To follow this tutorial you need the following components:

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Arduino IDE

We’ll program the ESP32-CAM using Arduino IDE, so make sure you have the ESP32 add-on installed.

Check the PHP URL

You should try to open the Raspberry Pi local IP address or your external example.com domain name, followed by /upload.php that should return:

Sorry, only JPG, JPEG, PNG & GIF files are allowed.Sorry, your file was not uploaded.
ESP32-CAM Testing the upload.php URL

If you see that message save your URL/domain name and path, your server should be ready and you can continue with this guide.

Additionally, try to access the /gallery.php path. You should get something as shown below:

ESP32-CAM Camera Board gallery PHP file view delete photos

ESP32-CAM Code

The next sketch posts the image to a server using HTTP POST. Copy the code below to your Arduino IDE.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-cam-post-image-photo-server/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <Arduino.h>
#include <WiFi.h>
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "esp_camera.h"

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

String serverName = "192.168.1.XXX";   // REPLACE WITH YOUR Raspberry Pi IP ADDRESS
//String serverName = "example.com";   // OR REPLACE WITH YOUR DOMAIN NAME

String serverPath = "/upload.php";     // The default serverPath should be upload.php

const int serverPort = 80;

WiFiClient client;

// CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27

#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22

const int timerInterval = 30000;    // time between each HTTP POST image
unsigned long previousMillis = 0;   // last time image was sent

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); 
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);  
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();
  Serial.print("ESP32-CAM IP Address: ");
  Serial.println(WiFi.localIP());

  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;

  // init with high specs to pre-allocate larger buffers
  if(psramFound()){
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 10;  //0-63 lower number means higher quality
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_CIF;
    config.jpeg_quality = 12;  //0-63 lower number means higher quality
    config.fb_count = 1;
  }
  
  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    delay(1000);
    ESP.restart();
  }

  sendPhoto(); 
}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= timerInterval) {
    sendPhoto();
    previousMillis = currentMillis;
  }
}

String sendPhoto() {
  String getAll;
  String getBody;

  camera_fb_t * fb = NULL;
  fb = esp_camera_fb_get();
  if(!fb) {
    Serial.println("Camera capture failed");
    delay(1000);
    ESP.restart();
  }
  
  Serial.println("Connecting to server: " + serverName);

  if (client.connect(serverName.c_str(), serverPort)) {
    Serial.println("Connection successful!");    
    String head = "--RandomNerdTutorials\r\nContent-Disposition: form-data; name=\"imageFile\"; filename=\"esp32-cam.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
    String tail = "\r\n--RandomNerdTutorials--\r\n";

    uint32_t imageLen = fb->len;
    uint32_t extraLen = head.length() + tail.length();
    uint32_t totalLen = imageLen + extraLen;
  
    client.println("POST " + serverPath + " HTTP/1.1");
    client.println("Host: " + serverName);
    client.println("Content-Length: " + String(totalLen));
    client.println("Content-Type: multipart/form-data; boundary=RandomNerdTutorials");
    client.println();
    client.print(head);
  
    uint8_t *fbBuf = fb->buf;
    size_t fbLen = fb->len;
    for (size_t n=0; n<fbLen; n=n+1024) {
      if (n+1024 < fbLen) {
        client.write(fbBuf, 1024);
        fbBuf += 1024;
      }
      else if (fbLen%1024>0) {
        size_t remainder = fbLen%1024;
        client.write(fbBuf, remainder);
      }
    }   
    client.print(tail);
    
    esp_camera_fb_return(fb);
    
    int timoutTimer = 10000;
    long startTimer = millis();
    boolean state = false;
    
    while ((startTimer + timoutTimer) > millis()) {
      Serial.print(".");
      delay(100);      
      while (client.available()) {
        char c = client.read();
        if (c == '\n') {
          if (getAll.length()==0) { state=true; }
          getAll = "";
        }
        else if (c != '\r') { getAll += String(c); }
        if (state==true) { getBody += String(c); }
        startTimer = millis();
      }
      if (getBody.length()>0) { break; }
    }
    Serial.println();
    client.stop();
    Serial.println(getBody);
  }
  else {
    getBody = "Connection to " + serverName +  " failed.";
    Serial.println(getBody);
  }
  return getBody;
}

View raw code

Before uploading the code, you need to insert your network credentials in the following variables:

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Make sure you select the right camera module. In this case, we’re using the AI-THINKER Model. If you’re using another camera model, you can read this Guide ESP32-CAM Camera Boards: Pin and GPIOs Assignment.

Add your Raspberry Pi IP address or use the server domain name:

String serverName = "192.168.1.XXX";   // REPLACE WITH YOUR Raspberry Pi IP ADDRESS
//String serverName = "example.com";   // OR REPLACE WITH YOUR DOMAIN NAME
String serverPath = "/upload.php";     // The default serverPath should be upload.php

Upload Code to ESP32-CAM

Now you can upload the code to your ESP32-CAM board. Connect the ESP32-CAM board to your computer using an FTDI programmer.

Follow the next schematic diagram:

Uploading the Code ESP32-CAM camera board new sketch upload code FTDI Programmer

Many FTDI programmers have a jumper that allows you to select 3.3V or 5V. Make sure the jumper is in the right place to select 5V.

Important: GPIO 0 needs to be connected to GND so that you’re able to upload code.

ESP32-CAMFTDI Programmer
GNDGND
5VVCC (5V)
U0RTX
U0TRX
GPIO 0GND

To upload the code, follow the next steps:

  1. Go to Tools > Board and select AI-Thinker ESP32-CAM.
  2. Go to Tools > Port and select the COM port the ESP32 is connected to.
  3. Then, click the upload button to upload the code.
  4. When you start to see these dots on the debugging window as shown below, press the ESP32-CAM on-board RST button.
ESP32-CAM camera board upload new Arduino IDE sketch press RESET button

After a few seconds, the code should be successfully uploaded to your board.

If you have troubles uploading the code, read our ESP32-CAM Troubleshooting Guide.

How the Code Works

Here’s a quick explanation on how the code works:

  • Imports all libraries;
  • Defines the needed variables;
  • Defines the camera pins;
  • In the setup() you establish a Wi-Fi connection and initialize the ESP32 camera.
  • The loop() has a timer that calls the sendPhoto() function every 30 seconds. You can change that delay time in the timerInterval variable.

The sendPhoto() function is the part that actually takes a photo and sends it to your server. You can use that function in other of your projects that require taking and publishing a photo to a server.

4. Testing and Final Demonstration

After uploading the code to your board, open the Arduino IDE Serial Monitor and you should see a similar message being printed every 30 seconds:

The file esp32-cam.jpg has been uploaded.
ESP32-CAM file esp32-cam.jpg has been uploaded Arduino IDE Serial Monitor testing project

If you go to your local server URL http://IP-Address/uploads, or to your cloud server URL http://example.com/uploads you should have a folder with all your stored photos.

ESP32-CAM Camera Board Uploads Folder Images stored server Demonstration

You can open each link to open a new page with the full image:

ESP32-CAM Upload Photo or Image to Server PHP Arduino IDE

Now, if you go to your local server URL http://IP-Address/gallery.php, or to your cloud server URL http://example.com/gallery.php, you can access the gallery page, where you can view and delete the photos.

ESP32-CAM Camera Photo Gallery view PHP file

To delete any photo, just click on the “Delete file” link next to each image.

ESP32-CAM delete file from gallery php

Wrapping Up

That’s it! Now, you can send your ESP32-CAM photos to any server using HTTP POST. Modify this project to best suit your needs, for example take a photo and send to a server when motion is detected.

Other ESP32 tutorials you might be interested in:

Learn more about the ESP32-CAM:

Thanks for reading.



Build Web Server projects with the ESP32 and ESP8266 boards to control outputs and monitor sensors remotely. Learn HTML, CSS, JavaScript and client-server communication protocols DOWNLOAD »

Build Web Server projects with the ESP32 and ESP8266 boards to control outputs and monitor sensors remotely. Learn HTML, CSS, JavaScript and client-server communication protocols DOWNLOAD »


Enjoyed this project? Stay updated by subscribing our newsletter!

49 thoughts on “ESP32-CAM Post Images to Local or Cloud Server using PHP (Photo Manager)”

  1. gallery.php seems horribly broken (and doesn’t work as supplied)
    Can you take a look at it and make sure it’s ok?
    Upload.php works just as you describe, but gallery.php is a no-go

    Reply
    • Hi Greg.
      I’m sorry about that.
      You are right. Something went wrong while copying the code.
      We fixed the problem, and it should be working now.
      Regards,
      Sara

      Reply
  2. gallery.php is working fine. I can view the pictures. upload.php is not working.
    192.168.86.xxx/upload.php give me the following….

    Sorry, only JPG, JPEG, PNG & GIF files are allowed.Sorry, your file was not uploaded.

    Reply
    • The same problem happened when pictures are uploaded to Bluehpost.
      gallery.php is working fine.

      upload.php is given the same problem.
      ………. Sorry, only JPG, JPEG, PNG & GIF files are allowed.Sorry, your file was not uploaded.

      Reply
      • Sorry. I found my own problem. My own mistake. Nothing wrong with upload.php file.

        TO access the uploaded picture files , I should type 192.168.86.xxx/uploads (Not 192.168.86.xxx/upload.php)

        Reply
  3. Hi Sara, how are you? One quick question: I like this project, but I would like to add some other features as follows:
    1) To allow a post a picture using a PIR;
    2) To allow to have live streaming as well.

    Is it to much complicated to get it ? Thanks a lot and I continue to stay tuned!

    Reply
  4. Hello,
    I keep getting an error message:
    ‘Sorry, there was an error uploading your file.’
    the connection to the server was successful.

    please help

    Reply
  5. Hi Everybody,

    if you use it with WAMP server, then set the file name without “:”, for example:

    date(‘Y_m_d_H_i_s_’, $datum)

    I hope it helps you too

    Reply
  6. Hi Sara, Rui,
    I have one question:
    At first, everything works fine, ESP32 is connected.
    But in the serial monitor are the following lines, I think there is something wrong:
    “connection successful!

    301 Moved Permanently

    Moved Permanently

    The document has moved here.

    Apache Server at turtle-home.de Port 80


    Do you have an idea, why it is not working as expected and instead of a picture the upper error is visible?
    Thanks in advance,
    Bernd

    Reply
  7. Sara,Rui,

    I have 2 ESP32-CAM and I want to setup two folders for uploading pictures, saving pictures in uploads and uploads1 directories. And I used serverPath = “/upload.php” and “/upload1.php” for ESP32-CAM .I also setup two php files, upload.php and uploads1.php in the /var/www/html directory.

    Only ESP32-CAM ,with uploads.php works.
    ESP32-CAM with upload1.php is not sending picture file to uploads1 directory, showing error message as follow….

    …..Sorry, there was an error uploading your file

    Please help.

    Reply
    • Maybe you need check the authority of the usr of apache.
      You can add “echo exec(‘whoami’);” in your upload.php, to see who is the usr.
      Then give it all permissions like this:
      sudo chown -R yourusr /var/www/html
      chmod -R 777 /var/www/html/

      Reply
  8. Hi Sara, how are you?

    One quick question: I like a lot this project, but I would like to add some other features as follows:
    1) To allow to post a picture using a PIR;
    2) To allow to have the live streaming as well.

    Is it to much complicated to get it ? Thanks a lot and I continue to stay tuned!

    Reply
  9. Hello there! Your tutorials are amazing! Can you please post a post example to a JSP or Java Servlet instead of a PHP? Thanks in advance!

    Reply
      • Hello Sara, actually i have coded the JSP servlet that is fully compatible with your current code. Maybe it helps someone, or maybe you can add it to the body of the article.

        JSP CODE, JUST CUSTOMIZE IMAGESPATH TO YOUR NEEDS:

        <%
        String imagesPath = “/opt/tomcat9/webapps/uploads/temp”;

        File file ;
        int maxFileSize = 5000 * 1024;
        int maxMemSize = 5000 * 1024;
        ServletContext context = pageContext.getServletContext();
        String filePath = imagesPath + “/”;

        // Verify the content type
        String contentType = request.getContentType();

        if ((contentType.indexOf(“multipart/form-data”) >= 0)) {
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // maximum size that will be stored in memory
        factory.setSizeThreshold(maxMemSize);

        // Location to save data that is larger than maxMemSize.
        factory.setRepository(new File(imagesPath));

        // Create a new file upload handler
        ServletFileUpload upload = new ServletFileUpload(factory);

        // maximum file size to be uploaded.
        upload.setSizeMax( maxFileSize );

        try {
        // Parse the request to get file items.
        List fileItems = upload.parseRequest(request);

        // Process the uploaded file items
        Iterator i = fileItems.iterator();

        while ( i.hasNext () ) {
        FileItem fi = (FileItem)i.next();
        if ( !fi.isFormField () ) {
        // Get the uploaded file parameters
        String fieldName = fi.getFieldName();
        String fileName = fi.getName();
        boolean isInMemory = fi.isInMemory();
        long sizeInBytes = fi.getSize();

        // Write the file
        if( fileName.lastIndexOf("\\") >= 0 ) {
        file = new File( filePath + fileName.substring( fileName.lastIndexOf("\\"))) ;
        } else {
        file = new File( filePath + fileName.substring(fileName.lastIndexOf("\\")+1)) ;
        }
        fi.write( file ) ;
        System.out.println("uploads/upload.jsp: Uploaded Filename: " + filePath + fileName);
        }
        }
        out.println("200");
        } catch(Exception ex) {
        System.out.println("uploads/upload.jsp: 1006-error");
        System.out.println(ex);
        out.println("500:ex1006");
        }

        } else {
        System.out.println(“uploads/upload.jsp: no file”);
        out.println(“500:ex-no-file”);
        }
        %>

        Reply
  10. hi, i am getting below error, can you help???

    400 Bad Request

    Bad Request

    Your browser sent a request that this server could not understand.

    Apache/2.4.10 (Debian) Server at 103.110.113.54 Port 80

    Reply
  11. Seems my commet didn’t showed up.

    I have this error:
    ESP32-CAM IP Address: 192.168.0.15
    Connecting to server: 192.168.0.19
    Connection successful!
    .

    400 Bad Request

    Bad Request

    Your browser sent a request that this server could not understand.

    Apache/2.4.38 (Raspbian) Server at 192.168.0.19 Port 80

    I’ve modified the upload.php file to change the “:” by “_” with no effect it seems.
    I’m stuck cause i don’t understand what is the issue.
    Can you help me here ?
    Thanks in advance !

    Reply
    • Solved this error. I’ve done a mistake by myself. Please ignore it.

      I’ve got another error; this time is seems the server is not getting the needed image format. At this point, don’t know if it is the script on the esp32 that is sending a wrong image format or the server side that has issue.

      Feel free to help 🙂

      Reply
  12. I’ve got an error; this time is seems the server is not getting the needed image format.

    At this point, don’t know if it is the script on the esp32 that is sending a wrong image format or the server side that has issue :

    Connecting to server: 192.168.0.19
    Connection successful!
    .

    Sorry, only JPG, JPEG, PNG & GIF files are allowed.Sorry, your file was not uploaded.

    Reply
  13. Hi,
    I have installed and configured a number of times now but sadly I still get the error :
    19:11:47.664 -> The file esp32-cam.jpg has not been uploaded. Sorry, there was an error uploading your file.
    19:11:49.157 -> Connecting to server: 192.168.1.216
    19:11:52.311 -> Connection successful!
    Has anyone any thoughts?
    Upload.php tells me the file is rejected due to it not being the correct image file type.
    Thanks
    D

    Reply
    • Doah!!
      It’s okay.
      I found MY error
      All works as expected now although my rpi is interminably s l o w
      All the best
      Keep safe
      David

      Reply
  14. Hi, Thanks for this super tutorial, I search about POST request to send parameter, but all tutorials using HttpClient to create the POST request, this ESP32-CAM Tutorial used WiFiClient to create the request. We can pass more parameters like temperature to get in PHP with $_POST?

    Reply
  15. My family keeps horses and our stables are out in the wilds without electricity and so lighting and tools are run from 12vdc lorry batteries and these are kept topped-up with solar panels. Could you perhaps publish a guide to running eg this project from a 12vdc battery and what you would consider to be an ideal psu and where to connect it – I am using the TTGO 800sim for the cloud server and this ESP32 cam to take images when a pir is tripped by wildlife – this saves to the sd card and sends to the server and displayed in the gallery as described above.

    Reply
  16. It works perfect with my old http server. Thank you! But it fails in case of https. Seems to be much more effort. I guess it would need something like WiFiClientSecure.h or is it much easier?

    Reply
  17. Hello,
    Everithing seem working fine, but no picture on the server !
    This below is what I get in the serial monitor every 30 secs :
    16:33:57.110 -> Connecting to server: http://www.ABCDEF.GH
    16:33:57.144 -> Connection successful!
    16:33:57.144 -> ..
    16:33:57.753 ->
    16:33:57.753 ->
    16:33:57.753 ->
    16:33:57.753 -> 301 Moved Permanently
    16:33:57.753 ->
    16:33:57.753 ->

    Moved Permanently

    16:33:57.753 ->

    The document has moved here.

    16:33:57.753 ->
    16:33:57.753 ->
    Is it something about server configuration ?

    Reply
  18. Hi Sara & Rui
    I would like to post these pictures to a website and want to use username and/or password protection.
    How would this be implemented in the ESP32 code ?
    Thanks for this brilliant tutorial.
    Regards

    Reply
  19. Upon some corrections the upload become 100% reliable with TinyGSM and BearSSL
    sslClient.print(tail);
    sslClient.flush();
    esp_camera_fb_return(fb);

    unsigned long timoutTimer = 10000;
    unsigned long startTimer = millis();
    boolean state = false;

    while ((startTimer + timoutTimer) > millis()) {
    Serial.print(".");
    delay(250);
    while (sslClient.available()) {
    char c = sslClient.read();

    Here sslClient.flush(); added, (Don’t just copy/paste, set an correct client name for your own sketch) the delay(100); set to delay(250), timoutTimer and startTimer set to “unsigned long” type.

    Reply
  20. Hi, I try to understand how the image is stored into the memory. I used 800×600 pixel (SVGA) setting. When the setting of No Function (s, 0) 39,117 bytes will be transmitted to the server. If I set to Grayscale (s, 2) 30,970 bytes will be transmitted to server. The values exclude head and tail sizes. I cannot relate these values with 800×600 (should be 480,000 bytes). If you can direct me to any information source, please do so.

    Reply
  21. Its unfortunate, but I did not not manage to setup according to the guide. My esp32 works fine with the default webcamserver program, which means the pin usage should be the same as your setup (CAMERA_MODEL_AI_THINKER), but somehow my PHP file is not getting the image. I get an error on the php saying:
    “[proxy_fcgi:error] [pid 11033:tid 139886463997696] [client 192.168.6.15:64826] AH01071: Got error ‘PHP message: PHP Warning: move_uploaded_file(uploads/2021.10.10_09:32:21_esp32-cam.jpg): failed to open stream: No such file or directory in /var/www/html/upload.php on line 50PHP message: PHP Warning: move_uploaded_file(): Unable to move ‘/var/www/html/tmp/phpuPQRSj’ to ‘uploads/2021.10.10_09:32:21_esp32-cam.jpg’ in /var/www/html/upload.php on line 50′”

    I am using ubuntu server and started playing around with tmp being in different places when I realized the image does not land in tmp no matter what I do even though the www-data user has permission to create a file.

    Then I was looking at the code and it seems somehow the image is not taken correctly or something similar. Any ideas?

    Reply
    • 16:36:10.149 -> Connection successful!
      16:36:10.181 -> ..
      16:36:10.394 ->
      16:36:10.394 -> 2e
      16:36:10.394 -> Sorry, there was an error uploading your file.
      16:36:10.394 -> 0
      16:36:10.394 ->
      16:36:10.394 ->

      Reply

Leave a Reply to Giancarlo Cancel reply

Download our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.