惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threat Research - Cisco Blogs
Google DeepMind News
Google DeepMind News
H
Help Net Security
H
Hackread – Cybersecurity News, Data Breaches, AI and More
MyScale Blog
MyScale Blog
Webroot Blog
Webroot Blog
Stack Overflow Blog
Stack Overflow Blog
T
The Blog of Author Tim Ferriss
D
Docker
L
LangChain Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Know Your Adversary
Know Your Adversary
A
About on SuperTechFans
U
Unit 42
NISL@THU
NISL@THU
M
MIT News - Artificial intelligence
T
The Exploit Database - CXSecurity.com
K
Kaspersky official blog
Martin Fowler
Martin Fowler
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Hacker News - Newest:
Hacker News - Newest: "LLM"
Engineering at Meta
Engineering at Meta
Blog — PlanetScale
Blog — PlanetScale
Scott Helme
Scott Helme
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 【当耐特】
WordPress大学
WordPress大学
Attack and Defense Labs
Attack and Defense Labs
P
Proofpoint News Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
TaoSecurity Blog
TaoSecurity Blog
B
Blog RSS Feed
小众软件
小众软件
G
Google Developers Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
SegmentFault 最新的问题
博客园 - 司徒正美
腾讯CDC
大猫的无限游戏
大猫的无限游戏
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Application and Cybersecurity Blog
Application and Cybersecurity Blog
S
Security @ Cisco Blogs
aimingoo的专栏
aimingoo的专栏
W
WeLiveSecurity
V
Vulnerabilities – Threatpost
N
News and Events Feed by Topic
Google Online Security Blog
Google Online Security Blog
Cisco Talos Blog
Cisco Talos Blog
C
Check Point Blog

Deno

Deno 2.8 | Deno Claw Patrol: an open-source security firewall for agents | Deno Fresh 2.3: Zero JS by default, View Transitions, and Temporal support | Deno Deno 2.7: Temporal API, Windows ARM, and npm overrides | Deno Build a dinosaur runner game with Deno, pt. 6 | Deno Build a dinosaur runner game with Deno, pt. 5 | Deno Deno Deploy is Generally Available | Deno Introducing Deno Sandbox | Deno Build a dinosaur runner game with Deno, pt. 4 | Deno Build a dinosaur runner game with Deno, pt. 3 | Deno Build a dinosaur runner game with Deno, pt. 2 | Deno React / Next.js Denial-of-Service Vulnerability: Deno Deploy users protected | Deno Deno 2.6: dx is the new npx | Deno Build a dinosaur runner game with Deno, pt. 1 | Deno React Server Functions / Next.js Vulnerability: Deno Deploy users protected | Deno My highlights from the new Deno Deploy | Deno Deno's Other Open Source Projects | Deno How Deno protects against npm exploits | Deno Help Us Raise $200k to Free JavaScript from Oracle | Deno Deno 2.5: Permissions in the config file | Deno Fresh 2.0 Graduates to Beta, Adds Vite Support | Deno Deno 2.4: deno bundle is back | Deno JavaScript™ Trademark Update | Deno What's coming to JavaScript | Deno A brief history of JavaScript | Deno Reports of Deno's Demise Have Been Greatly Exaggerated | Deno An Update on Fresh | Deno How Plaid migrated 100 services to a new database platform 5x faster with Deno | Deno Deno 2.3: Improved deno compile, local npm packages, and more | Deno Add JSR packages with pnpm and Yarn | Deno Zero-config Debugging with Deno and OpenTelemetry | Deno Exploring Art with TypeScript, Jupyter, Polars, and Observable Plot | Deno Deno v Oracle Update 3: Fighting the JavaScript Trademark | Deno Build a custom RAG AI agent in TypeScript and Jupyter | Deno How to get deep traces in your Node.js backend with OTel and Deno | Deno toranoana.deno #20 登録受付中(2025年3月14日) | Deno Node just added TypeScript support. What does that mean for Deno? | Deno The Dino 🦕, the Llama 🦙, and the Whale 🐋 | Deno Publish a lint rule, get a prize | Deno Deno 2.2: OpenTelemetry, Lint Plugins, node:sqlite | Deno If you're not using npm specifiers, you're doing it wrong | Deno How Deno's documentation is evolving | Deno Oracle justified its JavaScript trademark with Node.js—now it wants that ignored | Deno Introducing the JSR open governance board | Deno Intro to Wasm in Deno | Deno Announcing OpenAI on JSR | Deno Deno in 2024 | Deno Goodbye WinterCG, welcome WinterTC | Deno Build a SolidJS app with Deno | Deno Run your Next.js SSR app on Deno Deploy | Deno Solve Advent of Code 2024 with Deno and Win Prizes! | Deno Deno v. Oracle: Canceling the JavaScript Trademark | Deno Deno 2.1: Wasm Imports and other enhancements | Deno Build a Typesafe API with tRPC and Deno | Deno Self-contained Executable Programs with Deno Compile | Deno Build a Database App with Drizzle ORM and Deno | Deno Introducing your new JavaScript package manager: Deno | Deno Announcing Growthbook on JSR | Deno Build an Astro site with Deno | Deno How to convert CommonJS to ESM | Deno Announcing Deno 2 | Deno The Final Touches: What’s New In v2.0.0-rc.10 | Deno Announcing Stable V8 Bindings for Rust | Deno Deno 2.0 Release Candidate | Deno Secure, efficient private npm registries with Cloudsmith and Deno | Deno Painting the Plane as We Fly It: Designing JSR | Deno Introducing Web Cache API support on Deno Deploy | Deno Deno 1.46: The Last 1.x Release | Deno Protect your cloud spend with new Deno Deploy spend limits | Deno What we got wrong about HTTP imports | Deno Benchmarking AWS Lambda Cold Starts Across JavaScript Runtimes | Deno Announcing Supabase on JSR | Deno Deno 1.45: Workspace and Monorepo Support | Deno Introducing KV Backup for Deno Subhosting | Deno A Gentle Intro to TypeScript | Deno Announcing Hono on JSR | Deno How We Made the Deno Language Server Ten Times Faster | Deno How the Guardian uses Deno to audit accessibility and performance across their 2.7 million articles | Deno Introducing More Flexible Domain Association for Deno Subhosting | Deno The stabilization process of the Standard Library has begun | Deno Deno 1.44: Private npm registries, improved Node.js compat, and performance boosts | Deno How we built a secure, performant, multi-tenant cloud platform to run untrusted code | Deno The Deno Standard Library is now available on JSR | Deno How to document your JavaScript package | Deno Your Low Code Solution Needs an Escape Hatch | Deno Deno 1.43: Improved Language Server performance | Deno How Slack used Deno to save months of engineering effort in launching their new platform | Deno JSR Is Not Another Package Manager | Deno Announcing the Hookdeck SDK on JSR | Deno Announcing the Neon Serverless Driver on JSR | Deno An intro to TSConfig for JavaScript Developers | Deno How we built JSR | Deno How Netlify used Deno Subhosting to build a successful edge functions product | Deno Introducing Simpler Project Creation in Deno Deploy | Deno Deno 1.42: Better dependency management with JSR | Deno Introducing deployctl, the command line interface for Deno Deploy | Deno Introducing JSR - the JavaScript Registry | Deno How to add Monaco to a Next.js app and securely run untrusted user code | Deno Survey Results and Roadmap | Deno Deno 1.41: smaller deno compile binaries | Deno
Build a Simple Image Resizing API in less than 100 LOC | Deno
Andy Jiang · 2023-02-08 · via Deno

In this tutorial, we’ll build a simple image resizing API using ImageMagick and deploy it on Deno Deploy. The API will resize images by stretching or cropping them, although you can add more functionality later.

Follow the video tutorial for building an image resizing API.

Follow the video tutorial here.

View the source, try the demo, or check out the playground.

ImageMagick

We’ll use ImageMagick for all of the image manipulation in our API. It’s one of the most popular libraries for manipulating images, including resizing, format conversion, compression, and effect application. More specifically, we’ll use imagemagick_deno, which is ImageMagick compiled to WebAssembly and compatible with Deno and Deno Deploy.

Setting Up The Server

First, create a directory for your project. Then, create a new TypeScript file in that folder named main.ts.

In main.ts, let’s create a basic web server using std lib’s http module.

import { serve } from "https://deno.land/std@0.173.0/http/server.ts";

serve(
  async (req: Request) => {
    return new Response("Hello World!");
  },
);

Now, you can run this script by opening a terminal in this directory and running:

deno run --allow-net main.ts

Now, navigate to localhost:8000. You should see Hello World!.

Manipulating Images

The next step is to add image manipulation. In main.ts, let’s import a few things from imagemagick_deno:

import {
  ImageMagick,
  initializeImageMagick,
  MagickGeometry,
} from "https://deno.land/x/imagemagick_deno@0.0.14/mod.ts";

Then, let’s initialize ImageMagick, which sets the necessary configuration for the binary and for its API to work.

await initializeImageMagick();

Next, we’ll confirm the necessary parameters exist and make sense, or we’ll return a 400 (Bad Request) error code. We can access the querystring params with the searchParams function.

Let’s create a new function parseParams, which will:

  • check for necessary params image, height, and width
  • make sure that height and width are greater than 0 and less than 2048
  • return a string on error
function parseParams(reqUrl: URL) {
  const image = reqUrl.searchParams.get("image");
  if (image == null) {
    return "Missing 'image' query parameter.";
  }
  const height = Number(reqUrl.searchParams.get("height")) || 0;
  const width = Number(reqUrl.searchParams.get("width")) || 0;
  if (height === 0 && width === 0) {
    return "Missing non-zero 'height' or 'width' query parameter.";
  }
  if (height < 0 || width < 0) {
    return "Negative height or width is not supported.";
  }
  const maxDimension = 2048;
  if (height > maxDimension || width > maxDimension) {
    return `Width and height cannot exceed ${maxDimension}.`;
  }
  return {
    image,
    height,
    width,
  };
}

In our serve() function, we can access the querystring params and call parseParams:



serve(
  async (req) => {
    const reqURL = new URL(req.url);
    const params = parseParams(reqURL);
    if (typeof params === "string") {
      return new Response(params, { status: 400 });
    }
  },
);

Now that we have ensured that the necessary parameters exist, let’s get the image. Let’s create a new function getRemoteImage, which will fetch the image by URL, check that the mediaType is correct, and return a buffer and mediaType (or an error message). Note, we will need to import parseMediaType at the top of the file.

import { parseMediaType } from "https://deno.land/std@0.175.0/media_types/parse_media_type.ts";

async function getRemoteImage(image: string) {
  const sourceRes = await fetch(image);
  if (!sourceRes.ok) {
    return "Error retrieving image from URL.";
  }
  const mediaType = parseMediaType(sourceRes.headers.get("Content-Type")!)[0];
  if (mediaType.split("/")[0] !== "image") {
    return "URL is not image type.";
  }
  return {
    buffer: new Uint8Array(await sourceRes.arrayBuffer()),
    mediaType,
  };
}

Then, in our serve() function, after parseParams we can call getRemoteImage and handle any errors:



serve(
  async (req) => {
    const reqURL = new URL(req.url);
    const params = parseParams(reqURL);
    if (typeof params === "string") {
      return new Response(params, { status: 400 });
    }
    const remoteImage = await getRemoteImage(params.image);
    if (remoteImage === "string") {
      return new Response(remoteImage, { status: 400 });
    }
  },
);

Next, we can finally modify the image with ImageMagick. Let’s create a new function called modifyImage, which will accept imageBuffer and the params object.

In this function, we’ll use the MagickGeometry constructor to set the parameters for the resizing. Additionally, to allow for adaptable resizing, if either height or width is missing, we’ll set ignoreAspectRatio to false.

Finally, this function will return a Promise that will resolve to the transformed image as a Uint8Array buffer:

function modifyImage(
  imageBuffer: Uint8Array,
  params: { width: number; height: number; mode: string },
) {
  const sizingData = new MagickGeometry(
    params.width,
    params.height,
  );
  sizingData.ignoreAspectRatio = params.height > 0 && params.width > 0;
  return new Promise<Uint8Array>((resolve) => {
    ImageMagick.read(imageBuffer, (image) => {
      image.resize(sizingData);
      image.write((data) => resolve(data));
    });
  });
}

In serve(), after we call getRemoteImage, let’s add a call to modifyImage. Then, we can return a new Response, which contains the modifiedImage:



serve(
  async (req: Request) => {
    const reqURL = new URL(req.url);
    const params = parseParams(reqURL);
    if (typeof params === "string") {
      return new Response(params, { status: 400 });
    }
    const remoteImage = await getRemoteImage(params.image);
    if (remoteImage === "string") {
      return new Response(remoteImage, { status: 400 });
    }
    const modifiedImage = await modifyImage(remoteImage.buffer, params);
    return new Response(modifiedImage, {
      headers: {
        "Content-Type": remoteImage.mediaType,
      },
    });
  },
);

Congratulations! You have created an API for resizing images.

Next, let’s add more flexibility in the API and allow cropping.

Cropping Images

Standard resizing works in most cases, but there are some cases where you might want to crop images instead. Cropping will allow you to avoid squishing the image if you change the aspect ratio and remove unnecessary areas in the image.

In the parseParams function, let’s check for accepted modes (“crop” and “resize”), as well as add mode to the return object:

function parseParams(reqUrl: URL) {
  const image = reqUrl.searchParams.get("image");
  if (image == null) {
    return "Missing 'image' query parameter.";
  }
  const height = Number(reqUrl.searchParams.get("height")) || 0;
  const width = Number(reqUrl.searchParams.get("width")) || 0;
  if (height === 0 && width === 0) {
    return "Missing non-zero 'height' or 'width' query parameter.";
  }
  if (height < 0 || width < 0) {
    return "Negative height or width is not supported.";
  }
  const maxDimension = 2048;
  if (height > maxDimension || width > maxDimension) {
    return `Width and height cannot exceed ${maxDimension}.`;
  }
  const mode = reqUrl.searchParams.get("mode") || "resize";
  if (mode !== "resize" && mode !== "crop") {
    return "Mode not accepted: please use 'resize' or 'crop'.";
  }
  return {
    image,
    height,
    width,
    mode,
  };
}

Then, in the modifyImage function, if the mode is crop, we’ll use image.crop():

function modifyImage(
  imageBuffer: Uint8Array,
  params: { width: number; height: number; mode: "resize" | "crop" },
) {
  const sizingData = new MagickGeometry(
    params.width,
    params.height,
  );
  sizingData.ignoreAspectRatio = params.height > 0 && params.width > 0;
  return new Promise<Uint8Array>((resolve) => {
    ImageMagick.read(imageBuffer, (image) => {
      if (params.mode === "resize") {
        image.resize(sizingData);
      } else {
        image.crop(sizingData);
      }
      image.write((data) => resolve(data));
    });
  });
}

That is it! Now, if you want to try your API, just run deno run --allow-net main.ts, as we did before, and access it using a URL containing the width, height, image URL, and mode. For example, the URL localhost:8000/?image=https://deno.land/images/artwork/deno_city.jpeg&width=500&height=500 should give you something like this:

Squished resized image example

You have a working image resizer!

Deploying to Deno Deploy

You can host your app at the edge with Deno Deploy, our multi-tenant JavaScript serverless cloud.

First, create a GitHub repo containing main.ts. Yes, it can be a one file repo. Then, go to https://deno.com/deploy and connect your GitHub account. Create a new project from GitHub, and select the repo you just created. Select “GitHub Automatic”, which will deploy every time there’s a merge onto your main branch. Finally, your entrypoint should be main.ts.

Once you’ve connected, it should take up to a minute to deploy. Once it’s live, you can visit your URL.

What’s next?

Building an image resizing API with Deno and ImageMagick is not only simple and fast, but can be done within 100 lines of code. Not only that, but you can host this API globally at the edge, to minimize latency for your API’s consumers. You can extend this with more functionality like adding text or saving it into a storage service.

Got stuck or want to share what you’re working on? Come say hi on Discord or Twitter!