Get file extension from name – Javascript, PHP

Getting file extension from file name or path can be done in Javascript and PHP. I will show you the steps to get the file extension and you can apply them in any language you want.

Following code will get the file extension from file name or file path in Javascript:

const path = "https://adnan-tech.com/wp-content/uploads/2024/07/Manage-google-account.png"

const parts = path.split(".")

const extension = parts[parts.length - 1].toLowerCase()

console.log(extension)

Your parts variable will look like this:

Array split explode - Javascript, PHP
Array split explode – Javascript, PHP

Following code will get the file extension from file name or path in PHP:

$path = "https://adnan-tech.com/wp-content/uploads/2024/07/Manage-google-account.png";

$parts = explode(".", $path);

$extension = $parts[count($parts) - 1];

$extension = strtolower($extension);

echo $extension;

We are converting the extension to lower-case because it will be helpful when you want to perform some action based on file extension. For example:

if (extension == "jpg") {
    // do something if file is JPEG
}

Some files has their extension in upper-case and some in lower-case. So it would be good if you convert that to lower-case to check properly.

That’s how you can get the file extension from it’s name or path using Javascript and PHP.

How to compress image in PHP

Previously, I wrote an article on how to compress images in Node JS. In this article, we will discuss how you can compress an image in PHP.

Let’s say you have an image named “image.png”. The following code will compress the file by half:

$source = "image.jpg";
$destination = "image-min.jpg";
$quality = 50;

$info = getimagesize($source);
$image = null;

if ($info["mime"] == 'image/jpeg')
{
    $image = imagecreatefromjpeg($source);
}

else if ($info["mime"] == 'image/gif')
{
    $image = imagecreatefromgif($source);
}

else if ($info["mime"] == 'image/png')
{
    $image = imagecreatefrompng($source);
}

if ($image != null)
{
    imagejpeg($image, $destination, $quality);
    echo "Image compressed";
}

Explanation:

  • Line [1-3]: Setting the source and destination path. Also, the quality of the compressed image. It will be between 0 and 100. 0 means fully compressed and 100 indicates the original quality (although the file size will be reduced a little even on 100).
  • [5, 6]: getimagesize will return the size and mime type of the image. We will be needing the mime type only.
  • [8-21]: Creates a new image object based on file mime type.
  • [23-27]: If any of the supported image types match, create a JPEG image from source to destination along with the quality of the new image. If the source and destination paths are the same, the original image will be replaced by the compressed image. That is why we are using a different name for the destination image.

Why use JPEG for compressed image

You might be wondering why while creating an image object, we are checking the mime type of image. But on compressing the image we are only using imagejpeg that creates a JPEG file. We use JPEG (Joint Photographic Experts Group) for image compression for the following reasons:

  1. Compression: JPEG decreases the file size a lot without losing the quality of the image. You won’t see much difference in quality from the original to the compressed image but the file size will be much less than the original one.
  2. Custom quality: You can set the quality of your choice from 0 to 100.
  3. User experience: When the user opens a JPEG file and if it is too large, it will first load the lower quality version of the image, which is usually a grayscaled image. Then it loads the image in its original colors. This is pretty useful on slow internet.

Laravel

If you are working in Laravel, you might be facing difficulty because Laravel uses separate “storage” folder for handling all user uploaded files. Also, it creates a symbolic link to the public folder. So it gets a little confusing for developers on how to read image from storage and save the compressed image on storage.

Just change your source and destination variables to the following:

$source = base_path("public/storage/image.jpg");
$destination = base_path("storage/app/public/image-min.jpg");

To get the mime type of an image in Laravel, you can use the following code:

$file = request()->file("image");

if ($file->getClientMimeType() == 'image/jpeg')
{
    //
}

Complete code for Laravel developers will be:

if (request()->file("image"))
{
    $file = request()->file("image");

    $file_path = "images/" . $file->getClientOriginalName();
    $file->storeAs("/public", $file_path);

    $source = base_path("public/storage/" . $file_path);
    $destination = base_path("storage/app/public/" . $file_path);
    $quality = 50;

    // $info = getimagesize($source);
    $image = null;

    if ($file->getClientMimeType() == 'image/jpeg')
    {
        $image = imagecreatefromjpeg($source);
    }

    else if ($file->getClientMimeType() == 'image/gif')
    {
        $image = imagecreatefromgif($source);
    }

    else if ($file->getClientMimeType() == 'image/png')
    {
        $image = imagecreatefrompng($source);
    }

    if ($image != null)
    {
        imagejpeg($image, $destination, $quality);
        return "Image compressed";
    }
}

Note: Only the source and destination paths are changed and the way image mime type is get.

With that said, this concludes my article on how to compress an image in PHP. If you face any problem in following this, kindly do let me know.

Get user location in PHP

In this article, we will teach you, how you can get user location in PHP. We have a users table where we are saving the user’s location and the time when the last location was fetched.

CREATE TABLE users (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    location JSON NULL DEFAULT NULL,
    last_location_at TIMESTAMP NULL DEFAULT NULL
);

The most important columns are “location” and “last_location_at”. We will be fetching the user’s location every 24 hours. The following code will fetch the user location in PHP.

if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
{
    $client_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
else if (isset($_SERVER['HTTP_X_REAL_IP']))
{
    $client_ip = $_SERVER['HTTP_X_REAL_IP'];
}
else
{
    $client_ip = $_SERVER['REMOTE_ADDR'];
}

$user_agent = $_SERVER['HTTP_USER_AGENT'];

$timestamp = strtotime($user->last_location_at);

$current_timestamp = time();

$difference = $current_timestamp - $timestamp;

$twenty_four_hours_in_seconds = 24 * 60 * 60;

if ($difference >= $twenty_four_hours_in_seconds)
{
    $curl = curl_init();
    curl_setopt_array($curl, [
        CURLOPT_URL => "http://www.geoplugin.net/json.gp?ip=" . $client_ip,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode([]),
        CURLOPT_HTTPHEADER => [
            "Content-Type: application/json"
        ]
    ]);
    $response = curl_exec($curl);

    if (curl_errno($curl))
    {
        $error = curl_error($curl);
    }
    else
    {
        $response = json_decode($response);
        if ($response->geoplugin_status == 200)
        {
            $location = [
                "city" => $response->geoplugin_city,
                "continent" => $response->geoplugin_continentName,
                "continentCode" => $response->geoplugin_continentCode,
                "country" => $response->geoplugin_countryName,
                "countryCode" => $response->geoplugin_countryCode,
                "currencyCode" => $response->geoplugin_currencyCode,
                "currencySymbol" => $response->geoplugin_currencySymbol,
                "currencyConverter" => $response->geoplugin_currencyConverter,
                "latitude" => $response->geoplugin_latitude,
                "longitude" => $response->geoplugin_longitude,
                "region" => $response->geoplugin_region,
                "ipAddress" => $response->geoplugin_request,
                "timezone" => $response->geoplugin_timezone,
                "user_agent" => $user_agent
            ];
            $location = json_encode($location);

            // UPDATE `users` SET location = '$location', last_location_at = NOW() WHERE id = 2
        }
    }
    curl_close($curl);
}

Explanation:

  • Line 01-12: We are fetching the user’s IP address. When a user accesses the web server through a proxy or load balancer, the “REMOTE_ADDR” does not provide the actual IP address, rather it provides the IP address of the load balancer. That is why we are checking if the “HTTP_X_FORWARDED_FOR” or “HTTP_X_REAL_IP” headers are present. These headers are present when the user accesses the web server via proxy or load balancer.
  • Line 14: Getting the user’s device and browser information. It will tell if the user is using Chrome, Firefox, Safari, or whatever the browser. It will also tell the operating system he is using, if it is Mac OS, Windows, or Linux.
  • Line 16: Here we are getting the timestamp since the last location was fetched of that user. You might be getting this value from the database. If it is null, then it will return the seconds since 1970, which is good in case of null.
  • Line 18: Get the current timestamp in seconds.
  • Line 20: Get the difference between 2 times.
  • Line 22: Calculate how many seconds are there in 24 hours.
  • Line 24: Check if the time has passed 24 hours.
  • After that, I am calling a GeoPlugin API and passing the client’s IP address to it. I am saving all the required fields returned from API in a variable $location and converting that to JSON string. Then you can save that $location string in your database. Make sure to update the last_location_at to the current timestamp. This will make sure that the next location will be fetched after 24 hours.

One more thing:

You can show the user location on Google Maps too. You can follow this guide.

This feature has been practically implemented in an open-source Laravel boilerplate.

Get duration of video in PHP

If you are developing an application in PHP or any of its frameworks (e.g. Laravel) where you are allowing users to upload videos, and you want to get the duration of the video then this article is for you.

Getting the duration of user uploaded video has many benefits. One of them is you can limit the video duration. For example, you might have seen in TikTok that it allows videos of 15 or seconds only (for free users). To upload videos of longer length, you need to get their premium membership.

First, you need to install a library called getID3. You can install it by running the following command in your terminal:

composer require james-heinrich/getid3

I have a file saved at the following path:

http://127.0.0.1:8000/storage/video.mp4

The following PHP code will get the duration of the above video:

$remote_filename = "storage/" . $file_path;

if ($fp_remote = fopen($remote_filename, "rb"))
{
    $local_temp_filename = tempnam("/tmp", "getID3");

    if ($fp_local = fopen($local_temp_filename, "wb"))
    {
        while ($buffer = fread($fp_remote, 8192))
        {
            fwrite($fp_local, $buffer);
        }
        fclose($fp_local);

        $get_id3 = new \getID3;
        $file_info = $get_id3->analyze($local_temp_filename);

        $seconds = $file_info["playtime_seconds"];
        $seconds = round($seconds);

        return $seconds;
    }
}

Explanation:

  1. Line #3 will open the uploaded file in read-only binary mode.
  2. Line #5 will create a temporary file name in the directory “/tmp” (you do not need to create this directory). And the prefix of that file name will be “getID3”.
  3. Line #7 will open the temporary file in write-only binary mode.
  4. Line #9 reads the whole remote file in chunks of 8 KB per chunk. The loop continues until there is no more data, meaning the video file is read completely.
  5. Line #11 will write the buffer from the remote file to the local temporary file.
  6. Line #15 creates a new instance for class getID3. This class handles the metadata information for multimedia files like videos etc.
  7. Line #16 extracts the metadata information from local temporary file.
  8. Line #18 will get the seconds from the $file_info array.
  9. Finally, line #19 will round the number to the nearest whole number.

If you want to know how to get the size of uploaded file in PHP, you can check our this tutorial. Let me know if that works for you.

Search in all fields in DataTable

If you are using the DataTable Javascript library and are having trouble searching in all fields of the dataset except for the only one visible, then I will show you, how you can search in all fields of the DataTable.

By default, DataTable searches in all fields only on those fields that are visible on the table. Let’s say you have a dataset containing “name”, “designation”, and “country” fields. But you are displaying only “name”, and “designation” on the table.

<?php
    $data = [
        [ "name" => "Adnan", "designation" => "Software Engineer", "country" => "Pakistan" ],
        [ "name" => "John", "designation" => "Web Designer", "country" => "USA" ],
        [ "name" => "Burak", "designation" => "Cloud Engineer", "country" => "Turkey" ],
        [ "name" => "Sachin", "designation" => "Mobile Engineer", "country" => "India" ],
    ];
?>

<table id="example" class="display" style="width: 100%;">
    <thead>
        <tr>
            <th>Name</th>
            <th>Designation</th>
        </tr>
    </thead>
    
    <tbody>
        <?php foreach ($data as $d): ?>
            <tr>
                <td>
                    <?php echo $d["name"]; ?>
                </td>

                <td>
                    <?php echo $d["designation"]; ?>
                </td>
            </tr>
        <?php endforeach; ?>
    </tbody>
</table>

<script>
    new DataTable('#example')
</script>

Right now, you will be able to search by name and designation only because only these two are visible on the table.

But what if you want to search for country as well ?

You just need to create a hidden <div> element and render the complete dataset in it using json_encode PHP function.

<?php
    $data = [
        [ "name" => "Adnan", "designation" => "Software Engineer", "country" => "Pakistan" ],
        [ "name" => "John", "designation" => "Web Designer", "country" => "USA" ],
        [ "name" => "Burak", "designation" => "Cloud Engineer", "country" => "Turkey" ],
        [ "name" => "Sachin", "designation" => "Mobile Engineer", "country" => "India" ],
    ];
?>

<table id="example" class="display" style="width: 100%;">
    <thead>
        <tr>
            <th>Name</th>
            <th>Designation</th>
        </tr>
    </thead>
    
    <tbody>
        <?php foreach ($data as $d): ?>
            <tr>
                <td>
                    <div style="display: none;">
                        <?php echo json_encode($d); ?>
                    </div>
                    
                    <?php echo $d["name"]; ?>
                </td>

                <td>
                    <?php echo $d["designation"]; ?>
                </td>
            </tr>
        <?php endforeach; ?>
    </tbody>
</table>

<script>
    new DataTable('#example')
</script>

If you are trying to search the country, you will be able to see the records of that country, despite the country is not visible in that table.

That’s how you can search in all fields of the DataTable, despite the fact if they are visible or not.

Source code:

Download

CMD picking the wrong PHP version

Sometimes CMD picks the wrong version of PHP and not the one we install via XAMPP or MAMP.

Let’s say you have downloaded and installed XAMPP or MAMP with the latest PHP. But when you run the command “php -v” in your terminal, it displays the wrong version of PHP (typically the previously installed version of PHP). This is a common issue when you try to upgrade your PHP version. Previously, you could have upgraded the PHP version easily using Homebrew. But nowadays, Homebrew itself has become too complex and it has its errors.

Problem with Laravel 10

You will also face that error when you try to create a project in Laravel 10+ version. Because Laravel 10 requires PHP greater than 8 and a lot of developers are using PHP 7. The reason why developers are still using PHP 7 is because most of the shared hosting servers are still running on PHP 7. As PHP 8 is a big upgrade, a lot of things have been changed. So changing the PHP version could crash the entire project. On the local computer, it’s fine. But if you have a live website or an API, a crashed website means losing a lot of users and potentially a lot of customers which results in a loss of revenue (which no one wants).

Updating PHP version

To update the PHP version, you simply need to open CMD anywhere in your computer and run the following command:

export PATH="/Applications/MAMP/bin/php/php8.2.0/bin:$PATH"

Here, you can change the path to your own PHP file. Typically, it is in your XAMPP/MAMP “bin” folder. Inside “bin”, you will find the “php” folder. Inside that folder, you can go to your desired PHP version folder. And every PHP version folder also has a “bin” folder in it. Go it in and paste its path there.

The above line is for Mac users. For Windows users, the path will look like:

export PATH="C:\XAMPP\bin\php\php8.2.0\bin:$PATH"

Check PHP version

After that, you need to close the terminal and open it again. Then run the following command to verify the PHP version:

php -v

Now, you should be able to see your selected PHP version.

So that’s it. That’s how you can upgrade your PHP if your CMD is picking the wrong PHP version. If you face any issues with this, kindly do let me know.

Call class method from variable – PHP

Sometimes you have to call the class method from the PHP variable. Let’s say you have a class with few functions with arguments.

<?php

class User
{
	public function list()
	{
		echo "Users list";
	}
	
	public function fetch($id)
	{
		echo "User fetch: " . $id;
	}
	
	public function update($id, $name)
	{
		echo "User update: " . $id . ", " . $name;
	}
}

Then you can create the class object from a variable like the following:

<?php

class User
{
	public function list()
	{
		echo "Users list";
	}
	
	public function fetch($id)
	{
		echo "User fetch: " . $id;
	}
	
	public function update($id, $name)
	{
		echo "User update: " . $id . ", " . $name;
	}
}

$class_name = "User";
$class_obj = new $class_name();

Now to call the method from that object, you can do:

<?php

class User
{
	public function list()
	{
		echo "Users list";
	}
	
	public function fetch($id)
	{
		echo "User fetch: " . $id;
	}
	
	public function update($id, $name)
	{
		echo "User update: " . $id . ", " . $name;
	}
}

$class_name = "User";
$class_obj = new $class_name();

$action_name = "list";
$class_obj->{$action_name}(); // echo "Users list"

You can also pass parameters to the method as well.

<?php

class User
{
	public function list()
	{
		echo "Users list";
	}
	
	public function fetch($id)
	{
		echo "User fetch: " . $id;
	}
	
	public function update($id, $name)
	{
		echo "User update: " . $id . ", " . $name;
	}
}

$class_name = "User";
$class_obj = new $class_name();

$action_name = "fetch";
$class_obj->{$action_name}(3); // echo "User fetch 3"

You can call multiple parameters as well.

<?php

class User
{
	public function list()
	{
		echo "Users list";
	}
	
	public function fetch($id)
	{
		echo "User fetch: " . $id;
	}
	
	public function update($id, $name)
	{
		echo "User update: " . $id . ", " . $name;
	}
}

$class_name = "User";
$class_obj = new $class_name();

$action_name = "update";
$class_obj->{$action_name}(5, "Adnan"); // echo "User update 5, Adnan"

Full code:

<?php

class User
{
	public function list()
	{
		echo "Users list";
	}
	
	public function fetch($id)
	{
		echo "User fetch: " . $id;
	}

	public function update($id, $name)
	{
		echo "User update: " . $id . ", " . $name;
	}
}

$class_name = "User";
$class_obj = new $class_name();

$action_name = "list";
$class_obj->{$action_name}(); // echo "Users list"

echo "<br />";

$action_name = "fetch";
$class_obj->{$action_name}(3); // echo "User fetch 3"

echo "<br />";

$action_name = "update";
$class_obj->{$action_name}(5, "Adnan"); // echo "User update 5, Adnan"

I have used this technique to create a lightweight PHP framework. Check out our other tutorials on PHP.

TrustPilot clone – PHP, MySQL, Laravel

A clone of TrustPilot website is created in PHP and MySQL using Laravel framework version 11. For frontend rendering, I am using Vue JS 3 and on admin side I am using React JS.

Files included:

  • .php
  • .css
  • .js

Features:

  1. User can post reviews about a company.
  2. Can flag a review.
  3. Can share reviews on social media.
  4. Company owners can claim a company by verifying their email address.
  5. Automatically takes screenshot of a company home page.
  6. Admin can add companies from admin panel.
  7. Admin can view all reviews, flags and claims.

Demo:

Job Portal website in PHP and MySQL – MVC

A Job Portal website is created in PHP and MySQL using MVC architecture. MVC stands for Model-View-Controller and this architectural design is used by many websites for scalability.

Live Demo

  • ✅ Easy setup.
  • ✅ Compatible with almost every shared/dedicated/VPS hosting.
  • ✅ Free support.

New features:

  • ✅ Optimized sending bulk emails.

Files included:

  • .php
  • .css
  • .js

Tech stack:

  1. PHP +7.0
  2. MySQL +5.0
  3. Bootstrap 4
  4. Vue JS 3

Recruiter can post a job

Post job

Recruiter posted jobs

Recruiter uploaded jobs

Change status of applicant

Application status change

Edit/delete job

A recruiter can edit or delete any of his posted jobs at any time. This helps if the recruiter needs to change the requirements for a job or delete if the vacancy is already been filled.

Jobs Listing

Users will get a listing of all the jobs posted by recruiters.

Job listing

Job Detail

They can view the job details by clicking the job title.

Job detail

Filter Jobs

On the jobs listing page, users can filter jobs by the type of job they want, like developer, designer, etc. By the location to see if the job is in their city or if they can relocate to that city. Or by the nature of the job i.e. is the job part-time, full-time, or remote. Users can also search the jobs in their salary range. This way they can find jobs that pay them according to their needs.

Real-time updates on new jobs

Users will get real-time updates whenever a new job is posted by the recruiter. They do not have to refresh the page to check if there is any new job. To develop this feature, I have used sockets. You need to install Node JS in your system or server to make this feature work. Even if you do not have Node JS installed, all other features will work except for real-time job updates.

Email notifications of new jobs

Users can turn on notifications from recruiters. Whenever that recruiter posts a new job, all the users who have turned on their notifications will receive an email.

Admin can see all the stats

The admin of the website can see the total number of users registered. The total number of jobs that have been posted by recruiters. And the total number of applications that have been submitted for those jobs. The admin can see all this information on his dashboard.

Manage users

Admin will have the option to add new users to the website. While adding, he/she can select if the user be an applicant or a recruiter. Admin can also manage existing users. Admin can edit the user and can also delete the user if required. Admin can change the password of the user as well. This is helpful if the user is finding it difficult to receive the password reset email. Or if you want to prevent the user from logging in.

Deployment

This Job Portal website can be deployed on any server that supports PHP and MySQL.

Record and save audio from web – Javascript, Node JS, PHP

In this article, we are going to teach you, how you can record and save audio from web using Javascript. For saving audio files, we will teach you how you can do that with PHP and Node JS.

Let’s get started

First, we will create a folder named “record-audio-from-web”. Inside this folder, create a file named “index.html” and include fontawesome CDN in it. We will be displaying a microphone icon to start recording.

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />

Then we will create a microphone icon.

  1. Give it a unique ID so we can get it in Javascript.
  2. Attach an “onclick” listener that will be called whenever that icon is clicked.
  3. And set the cursor to pointer so user will know that this is clickable.
<i class="fa fa-microphone" id="icon-record-audio" onclick="recordAudio()" style="cursor: pointer;"></i>

Now in Javascript, we first have to create an instance of recorder.

let recorder = null

Get microphone permission

Then we will create that function that will be called when the microphone icon is clicked. This function will:

  1. Get the permission to access laptop’s microphone.
  2. Initialize a media recorder object.
  3. Start recording audio from microphone.
// function to record audio
async function recordAudio() {
    // get permission to access microphone
	navigator.permissions.query({name: 'microphone'})
		.then(function (permissionObj) {
			console.log(permissionObj.state)				
		})
		.catch(function (error) {
			console.log('Got error :', error);
		})

    // get recorder object
	recorder = await doRecordAudio()

    // start audio
	recorder.start()
}

Record audio

Then we will create “doRecordAudio” function that will handle all the recording and stoping mechanism.

function doRecordAudio() {
    return new Promise(function (resolve) {
        // get user audio media
        navigator.mediaDevices.getUserMedia({
            audio: true
        })
        .then(function (stream) {
            // [media recorder goes here]
        })
    })
}

After that, we need to create a media recorder object. Following code goes in the [media recorder goes here] section:

// create media recorder object
const mediaRecorder = new MediaRecorder(stream)

// save audio chunks in an array
const audioChunks = []
mediaRecorder.addEventListener("dataavailable", function (event) {
    audioChunks.push(event.data)
})

// [start and stop listener goes here]

This will create a media recorder object from stream and save all audio chunks (when available) in an array.

Then write the following code in [start and stop listener goes here] section to create start and stop listeners:

// create a start function
const start = function () {
    // [start listener goes here]
}

// create a stop function
const stop = function () {
    return new Promise(function (resolve) {
        // [stop listener goes here]
    })
}

// send the values back to promise
resolve({
    start,
    stop
})

This “start” function gets called from “recordAudio()” function.

Start recording audio

When the start function is called, we need to:

  1. Change the microphone icon to stop icon.
  2. Change onclick listener, so next time the icon is clicked, we will stop the recording.
  3. And play the audio.
  4. Tell the media to start recording.

Following code goes in the [start listener goes here] section:

// when recording starts, set the icon to stop
document.getElementById("icon-record-audio").className = "fa fa-stop-circle"

// on icon clicked
document.getElementById("icon-record-audio").onclick = async function() {
    // stop the recorder
    if (recorder != null) {
        const audio = await recorder.stop()

        // [play audio]

        // [get audio base64 string]
    }
}

// start media recorder
mediaRecorder.start()

If you test now, you will be able to record the audio. The microphone icon will be changed as well. But now you need to find a way to stop the recording.

Stop the recording

When you are done recording the audio, you can click the icon again to stop it. Following code goes in the [stop listener goes here] section. This will:

  1. Attach a listener that will be called when the recording is stopped.
  2. Change the icon back to microphone.
  3. Reset the onclick listener to its initial state.
  4. Convert the audio chunks array into a blob.
  5. Create URL object from blob.
  6. Create an audio object to play when required.
  7. And stop the media recorder.
// on recording stop listener
mediaRecorder.addEventListener("stop", function () {

    // change the icon back to microphone
    document.getElementById("icon-record-audio").className = "fa fa-microphone"

    // reset the onclick listener so when again clicked, it will record a new audio
    document.getElementById("icon-record-audio").onclick = async function() {
        recordAudio()
    }

    // convert the audio chunks array into blob
    const audioBlob = new Blob(audioChunks)

    // create URL object from URL
    const audioUrl = URL.createObjectURL(audioBlob)

    // create an audio object to play
    const audio = new Audio(audioUrl)
    const play = function () {
        audio.play()
    }

    // send the values back to the promise
    resolve({
        audioBlob,
        play
    })
})

// stop the media recorder
mediaRecorder.stop()

If you test now, you can record the audio and stop the audio. The icon will change from microphone to stop and then from stop to microphone.

Play the audio after recording

In order to play the audio you just recorded, write the following line in the [play audio] section:

audio.play()

“stop” function will return the promise containing the “new Audio” object and calling “play()” will play the audio.

Converting audio to base64

We are already sending audioBlob from “stop” function, so we can create base64 string from it. Write the following code in your [get audio base64 string] section:

// get audio stream
const reader = new FileReader()
reader.readAsDataURL(audio.audioBlob)
reader.onloadend = function() {
    // get base64
    let base64 = reader.result

    // get only base64 data
    base64 = base64.split(',')[1]

    // send base64 to server to save
    sendVoiceNote(base64)
}

This will create a base64 string fom audio.

Sending audio base64 to server

Now we will create a function that will call an AJAX to save this base64 string as audio.

function sendVoiceNote(base64) {
        // create an instance for AJAX
        var ajax = new XMLHttpRequest()

        // set request method as POST, set URL and set asynchronous to true
        ajax.open("POST", "http://localhost:8888/tutorials/record-audio-from-web/sendVoiceNote.php", true)

        // send base64 string to server
        const formData = new FormData()
        formData.append("base64", base64)
        ajax.send(formData)
}

Note: In your server, create a folder named “audios” where all audio files will be saved.

Saving audio file on server – PHP

First, we will teach you how can you save it using PHP. Create a file named “sendVoiceNote.php” and write the following code in it:

<?php

$base64 = $_POST["base64"];

$file_name = "audios/" . time() . ".webm";
file_put_contents($file_name, base64_decode($base64));

echo $file_name;

Try recording an audio again. When you stop the audio, you will see that a new file will be saved in your “audios” folder. You can listen to your audio in your favorite media player (VLC etc).

Saving audio file on server – Node JS

Now we will teach you, how you can save the file using Node JS. Create a Node JS project by running the following commands one-by-one:

> npm install express http express-formidable fs cors
> npm install -g nodemon
> nodemon server.js

Create a file named “server.js” and write the following code in it:

const express = require("express")
const app = express()

const expressFormidable = require("express-formidable")
app.use(expressFormidable())

const cors = require("cors")
app.use(cors())

const fileSystem = require("fs")
const http = require("http").createServer(app)

http.listen(3000, function () {
	app.post("/sendVoiceNote", async function (request, result) {
		const base64 = request.fields.base64

        const buffer = Buffer.from(base64, "base64")
        const voiceNote = "audios/" + new Date().getTime() + ".webm"
        await fileSystem.writeFileSync(voiceNote, buffer)

        result.send(voiceNote)
	})
})

In your client side, change the AJAX “open” function call from:

ajax.open("POST", "http://localhost/record-audio-from-web/sendVoiceNote.php", true)

To:

ajax.open("POST", "http://localhost:3000/sendVoiceNote", true)

So that’s it. That’s how you can record audio from web and save it in your server using Node JS or PHP. If you face any problem in following this, kindly do let me know.

Here is the complete code of all files:

index.html

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
<i class="fa fa-microphone" id="icon-record-audio" onclick="recordAudio()" style="cursor: pointer;"></i>

<script>

    let recorder = null

    function sendVoiceNote(base64) {
        // create an instance for AJAX
        var ajax = new XMLHttpRequest()

        // set request method as POST, set URL and set asynchronous to true
        // ajax.open("POST", "http://localhost:3000/sendVoiceNote", true)
        ajax.open("POST", "http://localhost/record-audio-from-web/sendVoiceNote.php", true)

        // whenever the status of request is changed
        // ajax.onreadystatechange = async function() {
        //     if (this.readyState == 4) {
        //         if (this.status == 200) {
        //             console.log(this.responseText)
        //         }

        //         if (this.status == 500) {
        //             console.log(this.responseText)
        //         }
        //     }
        // }

        // send base64 string to server
        const formData = new FormData()
        formData.append("base64", base64)
        ajax.send(formData)
    }

	function doRecordAudio() {
        return new Promise(function (resolve) {
            // get user audio media
            navigator.mediaDevices.getUserMedia({
                audio: true
            })
            .then(function (stream) {
                // create media recorder object
                const mediaRecorder = new MediaRecorder(stream)

                // save audio chunks in an array
                const audioChunks = []
                mediaRecorder.addEventListener("dataavailable", function (event) {
                    audioChunks.push(event.data)
                })

                // create a start function
                const start = function () {

                    // when recording starts, set the icon to stop
                    document.getElementById("icon-record-audio").className = "fa fa-stop-circle"

                    // on icon clicked
                    document.getElementById("icon-record-audio").onclick = async function() {
                        // stop the recorder
                        if (recorder != null) {
                            const audio = await recorder.stop()

                            // play the audio
                            audio.play()

                            // get audio stream
                            const reader = new FileReader()
                            reader.readAsDataURL(audio.audioBlob)
                            reader.onloadend = function() {
                                // get base64
                                let base64 = reader.result

                                // get only base64 data
                                base64 = base64.split(',')[1]

                                // send base64 to server to save
                                sendVoiceNote(base64)
                            }
                        }
                    }

                    // start media recorder
                    mediaRecorder.start()
                }

                // create a stop function
                const stop = function () {
                    return new Promise(function (resolve) {

                        // on recording stop listener
                        mediaRecorder.addEventListener("stop", function () {

                            // change the icon back to microphone
                            document.getElementById("icon-record-audio").className = "fa fa-microphone"

                            // reset the onclick listener so when again clicked, it will record a new audio
                            document.getElementById("icon-record-audio").onclick = async function() {
                                recordAudio()
                            }

                            // convert the audio chunks array into blob
                            const audioBlob = new Blob(audioChunks)

                            // create URL object from URL
                            const audioUrl = URL.createObjectURL(audioBlob)

                            // create an audio object to play
                            const audio = new Audio(audioUrl)
                            const play = function () {
                                audio.play()
                            }

                            // send the values back to the promise
                            resolve({
                                audioBlob,
                                play
                            })
                        })

                        // stop the media recorder
                        mediaRecorder.stop()
                    })
                }

                // send the values back to promise
                resolve({
                    start,
                    stop
                })
            })
        })
    }

    // function to record audio
	async function recordAudio() {
        // get permission to access microphone
		navigator.permissions.query({name: 'microphone'})
			.then(function (permissionObj) {
				console.log(permissionObj.state)				
			})
			.catch(function (error) {
				console.log('Got error :', error);
			})

        // get recorder object
		recorder = await doRecordAudio()

        // start audio
  		recorder.start()
	}
</script>

server.js

const express = require("express")
const app = express()

const expressFormidable = require("express-formidable")
app.use(expressFormidable())

const cors = require("cors")
app.use(cors())

const fileSystem = require("fs")
const http = require("http").createServer(app)

http.listen(3000, function () {
	app.post("/sendVoiceNote", async function (request, result) {
		const base64 = request.fields.base64

        const buffer = Buffer.from(base64, "base64")
        const voiceNote = "audios/" + new Date().getTime() + ".webm"
        await fileSystem.writeFileSync(voiceNote, buffer)

        result.send(voiceNote)
	})
})

sendVoiceNote.php

<?php

$base64 = $_POST["base64"];

$file_name = "audios/" . time() . ".webm";
file_put_contents($file_name, base64_decode($base64));

echo $file_name;