7.4: Hosting Photos on Cloudinary

We need to setup photo uploading capability for the user. Right now, photos are just urls to content that we have no control over. We can't upload more photos to randomuser.me. We could have users upload photos somewhere else and have them provide a link - it would be more simple for us to implement - but, it's not great from the user's perspective.

Another option is that we could store the users as binary blobs in our database. We'd need to really limit file size and our user base so we could keep hosting costs low when we deploy. Databases are very inefficient ways to store things like photos.

We could also store photographs in our file system and link to them through their paths rather than URLs. The problem is also disk space and the possibility of exceeding the size for free accounts when we deploy it.

The last option is using a cloud provider and that's what we'll be using. We'll be using Cloudinary. It has a pretty good free tier and is pretty manageable to work with.

Overview of our Process

The downside to working with a cloud provider is that there are some extra steps in the process of uploading files. It is a good exercise, however. It's important to keep in mind how data is moving around after a user clicks "upload".

To help you understand, here is a diagram and an overview of the photo uploading process that that we'll implement:

CloudinaryFlow
  1. Client makes a request to upload a photo to our API, passing along a JWT

  2. Our back-end then uploads the photo to Cloudinary, passing along an API key and API secret

  3. Cloudinary stores the photo and sends back response to our API containing the URL and a public ID for the photo.

  4. Our API saves the photo URL and public ID to our database.

  5. The photo is saved in our database and given a SQL ID.

  6. Our API returns a 201 Created with the URL of the uploaded photo.

Registering for a Cloudinary Account

Head to www.cloudinary.com and register for an account. Browse around the docs. Especially two places:

After registering, you should be directed to your dashboard.

In Account Details - you should see three pieces of information you'll need:

  1. Cloud name

  2. API Key

  3. API Secret - click "reveal"

CloudinaryDashboard

Storing Our Cloudinary Information

Open up the appsettings.json file in your .API project and add a new section called Cloudinary Settings

},
"CloudinarySettings": {
    "CloudName": "",
    "ApiKey": "",
    "ApiSecret": ""
}

Add the CloudName, ApiKey, and ApiSecret from your Cloudinary dashboard.

If you didn't do it when we added our Token key and you would like to protect your API information, you can enter this command in git to ignore changes made to it:

git update-index --assume-unchanged appsettings.json

Add Cloudinary file

In your .Helpers project, add a new class called CloudinarySetting.cs with three properties that match what's above our configuration:

public class CloudinarySettings
{
    public string CloudName { get; set; }
    public string ApiKey { get; set; }
    public string ApiSecret { get; set; }
}

Next, we need to add a reference to our .Helpers project in our .API project. Add this to the ItemGroup with ProjectReferences:

<ItemGroup>
    <ProjectReference Include="..\EFConnect.Data\EFConnect.Data.csproj" />
    <ProjectReference Include="..\EFConnect.Models\EFConnect.Models.csproj" />
    <ProjectReference Include="..\EFConnect.Contracts\EFConnect.Contracts.csproj" />
    <ProjectReference Include="..\EFConnect.Services\EFConnect.Services.csproj" />
    <ProjectReference Include="..\EFConnect.Helpers\EFConnect.Helpers.csproj" />
</ItemGroup>

Now, we'll add the CloudinarySettings to the ConfigureServices() method in our Startup.cs file:

services.Configure<CloudinarySettings>(Configuration.GetSection("CloudinarySettings")); // <--- added
services.AddTransient<Seed>();
services.AddScoped<IAuthService, AuthService>();
services.AddScoped<IUserService, UserService>();

Add PublicId Property to Photo Class

In the Photo.cs file in your .Data/Entities/ folder, add a property for the PublicId:

public DateTime DateAdded { get; set; }

public bool IsMain { get; set; }

public string PublicId { get; set; }            // <--- Added

Now, since we've added a new class - we need to run a new migration.

In your EFConnect.Data project, run the following command:

dotnet ef migrations add AddedPublicId -s "../EFConnect.API/EFConnect.API.csproj"

Then:

dotnet ef database update

Add Cloudinary NuGet Package

Open the Command Palette in VS Code (ctrl + shift + p) and type in NuGet Package Manager: Add Package.

Select that and then type in CloudinaryDotNet.

You'll then be prompted to choose a version number. The version used in this tutorial is 1.2.0. If there's a later version when you're doing this tutorial - it may or may not work the same. I'd go with 1.2.0 unless you feel pretty comfortable working with the docs if you run into an issue.

It will then ask you to choose your project - choose the .API project.

VS Code should then prompt you to restore your project. Click restore. If it doesn't prompt you run a dotnet restore in the .API project.

<ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5"/>
    <PackageReference Include="CloudinaryDotNet" Version="1.2.0"/>
</ItemGroup>

Last updated