图像服务 API
astro:assets 旨在使任何图像优化服务都能轻松地在 Astro 上构建服务。
什么是图像服务?
Section titled “什么是图像服务?”Astro 提供两种类型的图像服务:本地和外部。
- 本地服务在构建静态站点时直接处理图像转换,或者在运行时同时处理开发模式和按需渲染中的图像转换。这些通常是围绕 Sharp、ImageMagick 或 Squoosh 等库的封装。在开发模式和按需渲染的生产路径中,本地服务使用 API 端点进行转换。
 - 外部服务指向 URL,可以添加对 Cloudinary、Vercel 或任何符合 RIAPI 的服务器等服务的支持。
 
使用图像服务 API 构建
Section titled “使用图像服务 API 构建”服务定义采用导出默认对象的形式,具有各种必需的方法(“钩子”)。
外部服务提供一个 getURL(),它指向输出 <img> 标记的 src。
本地服务为开发模式和按需渲染提供 transform() 方法来对图像执行转换,并提供 getURL() 和 parseURL() 方法来使用端点。
两种类型的服务都可以提供 getHTMLAttributes() 来确定输出 <img> 的其他属性,并提供 validateOptions() 来验证和增强传递的选项。
外部服务指向一个远程 URL,用作最终 <img> 标记的 src 属性。该远程 URL 负责下载、转换和返回图像。
import type { ExternalImageService, ImageTransform, AstroConfig } from "astro";
const service: ExternalImageService = {  validateOptions(options: ImageTransform, imageConfig: AstroConfig['image']) {    const serviceConfig = imageConfig.service.config;    // 强制用户设置最大宽度。    if (options.width > serviceConfig.maxWidth) {      console.warn(`Image width ${options.width} exceeds max width ${serviceConfig.maxWidth}. Falling back to max width.`);      options.width = serviceConfig.maxWidth;    }
    return options;  },  getURL(options, imageConfig) {    return `https://mysupercdn.com/${options.src}?q=${options.quality}&w=${options.width}&h=${options.height}`;  },  getHTMLAttributes(options, imageConfig) {    const { src, format, quality, ...attributes } = options;    return {      ...attributes,      loading: options.loading ?? 'lazy',      decoding: options.decoding ?? 'async',    };  }};
export default service;要创建自己的本地服务,可以指向内置端点(/_image),或者你还可以创建自己的端点来调用服务的方法。
import type { LocalImageService, AstroConfig } from "astro";
const service: LocalImageService = {  getURL(options: ImageTransform, imageConfig: AstroConfig['image']) {    const searchParams = new URLSearchParams();    searchParams.append('href', typeof options.src === "string" ? options.src : options.src.src);    options.width && searchParams.append('w', options.width.toString());    options.height && searchParams.append('h', options.height.toString());    options.quality && searchParams.append('q', options.quality.toString());    options.format && searchParams.append('f', options.format);    return `/my_custom_endpoint_that_transforms_images?${searchParams}`;    // 或者使用内置端点,它将调用你的 parseURL 和 transform 函数:    // return `/_image?${searchParams}`;  },  parseURL(url: URL, imageConfig) {    return {      src: params.get('href')!,      width: params.has('w') ? parseInt(params.get('w')!) : undefined,      height: params.has('h') ? parseInt(params.get('h')!) : undefined,      format: params.get('f'),      quality: params.get('q'),    };  },  transform(buffer: Uint8Array, options: { src: string, [key: string]: any }, imageConfig): { data: Uint8Array, format: OutputFormat } {    const { buffer } = mySuperLibraryThatEncodesImages(options);    return {      data: buffer,      format: options.format,    };  },  getHTMLAttributes(options, imageConfig) {    let targetWidth = options.width;    let targetHeight = options.height;    if (typeof options.src === "object") {      const aspectRatio = options.src.width / options.src.height;
      if (targetHeight && !targetWidth) {        targetWidth = Math.round(targetHeight * aspectRatio);      } else if (targetWidth && !targetHeight) {        targetHeight = Math.round(targetWidth / aspectRatio);      }    }
    const { src, width, height, format, quality, ...attributes } = options;
    return {      ...attributes,      width: targetWidth,      height: targetHeight,      loading: attributes.loading ?? 'lazy',      decoding: attributes.decoding ?? 'async',    };  },  propertiesToHash: ['src', 'width', 'height', 'format', 'quality'],};export default service;在构建静态站点和预渲染路由时,<Image /> 和 getImage(options) 都会调用 transform() 函数。它们分别通过组件属性或 options 参数传递选项。转换后的图像将构建到 dist/_astro 文件夹中。它们的文件名将包含传递给 propertiesToHash 属性的哈希值。此属性是可选的,默认为 ['src', 'width', 'height', 'format', 'quality']。如果你的自定义图像服务有更多可以更改生成图像的选项,请将它们添加到数组中。
在开发模式和使用适配器进行按需渲染时,Astro 并不提前知道哪些图像需要优化。Astro 使用 GET 端点(默认情况下为 /_image )在运行时处理图像。 <Image /> 和 getImage() 将它们的选项传递给 getURL(),后者将返回端点 URL。然后,端点调用 parseURL() 并将结果属性传递给 transform()。
getConfiguredImageService 和 imageConfig
Section titled “getConfiguredImageService 和 imageConfig”如果你将自己的端点实现为 Astro 端点,则可以使用 getConfiguredImageService 和 imageConfig 来调用服务的 parseURL 和 transform 方法并提供服务的配置对象。
要访问图像服务配置(image.service.config),你可以使用 imageConfig.service.config。
import type { APIRoute } from "astro";import { getConfiguredImageService, imageConfig } from 'astro:assets';
export const GET: APIRoute = async ({ request }) => {  const imageService = await getConfiguredImageService();
  const imageTransform = imageService.parseURL(new URL(request.url), imageConfig);  // ... 从 imageTransform.src 获取图像并将其存储在 inputBuffer 中  const { data, format } = await imageService.transform(inputBuffer, imageTransform, imageConfig);  return new Response(data, {      status: 200,      headers: {        'Content-Type': mime.getType(format) || ''      }    }  );}请参阅内置端点 了解完整示例。
getURL()
Section titled “getURL()”本地和外部服务所需
getURL(options: ImageTransform, imageConfig: AstroConfig['image']): string
对于本地服务,该钩子返回生成图像的端点的 URL(在按需渲染和开发模式下)。在构建期间它不可用。 getURL() 指向的本地端点可以调用 parseURL() 和 transform()。
对于外部服务,该钩子返回图像的最终 URL。
对于这两种类型的服务,options是用户作为 <Image /> 组件的属性或 getImage() 选项传递的属性。它们属于以下类型:
export type ImageTransform = {    // ESM 导入的图像 | 远程/公共图像路径    src: ImageMetadata | string;    width?: number;    height?: number;    widths?: number[] | undefined;      densities?: (number | `${number}x`)[] | undefined;    quality?: ImageQuality;    format?: OutputFormat;    alt?: string;    [key: string]: any;};parseURL()
Section titled “parseURL()”本地服务所需;外部服务不可用
parseURL(url: URL, imageConfig: AstroConfig['image']): { src: string, [key: string]: any}
该钩子通过 getURL() 将生成的 URL 解析成一个对象,该对象具有可供 transform 使用的不同属性(在按需渲染和开发模式下)。在构建期间它不可用。
transform()
Section titled “transform()”仅本地服务需要;外部服务不可用
transform(buffer: Uint8Array, options: { src: string, [key: string]: any }, imageConfig: AstroConfig['image']): { data: Uint8Array, format: OutputFormat }
该钩子转换并返回图像,并在构建过程中被调用以创建最终的资源文件。
你必须返回 format 以确保在按需渲染和开发模式下向用户提供正确的 MIME 类型。
getHTMLAttributes()
Section titled “getHTMLAttributes()”本地和外部服务可选
getHTMLAttributes(options: ImageTransform, imageConfig: AstroConfig['image']): Record<string, any>
该钩子根据用户传递的参数 (options) 返回用于将图像呈现为 HTML 的所有附加属性。
getSrcSet()
Section titled “getSrcSet()”
	添加于:
	astro@3.3.0
	
	
对于本地和外部服务而言是可选的。
getSrcSet?: (options: ImageTransform, imageConfig: AstroConfig['image']): SrcSetValue[] | Promise<SrcSetValue[]>;
这个钩子函数会生成指定图像的多个变体,例如,在 <img> 或 <picture> 的 source 上生成 srcset 属性。
该钩子函数返回一个包含以下属性的对象数组:
export type SrcSetValue = {  transform: ImageTransform;  descriptor?: string;  attributes?: Record<string, any>;};validateOptions()
Section titled “validateOptions()”本地和外部服务可选
validateOptions(options: ImageTransform, imageConfig: AstroConfig['image']): ImageTransform
该钩子允许你验证和增强用户传递的选项。对于设置默认选项或告诉用户参数必传来说,这非常有用。
查看 Astro 的内置服务中是如何使用 validateOptions() 的 。
在 astro.config.mjs 中配置要使用的图像服务。配置采用以下形式:
import { defineConfig } from "astro/config";
export default defineConfig({  image: {    service: {      entrypoint: "your-entrypoint", // 'astro/assets/services/sharp' | string,      config: {        // 特定的服务配置,可选。      }    }  },});Astro 提供了许多助手函数,可用于部署定制化的图像服务。这些工具函数可使用 astro/assets/utils 导入:
import {    isRemoteAllowed,    matchHostname,    matchPathname,    matchPattern,    matchPort,    matchProtocol,    isESMImportedImage,    isRemoteImage,    resolveSrc,    imageMetadata,    emitESMImage,    getOrigQueryParams,    inferRemoteSize,    propsToFilename,    hashTransform} from "astro/assets/utils";isRemoteAllowed()
Section titled “isRemoteAllowed()”类型: (src: string, { domains, remotePatterns }: {domains: string[], remotePatterns: RemotePattern[] }): boolean
astro@4.0.0
	
	
判断一个给定的远程资源(由其源 URL 所识别)是否被允许,是基于指定的域名(domain)和远程规则(remote pattern)。
import { isRemoteAllowed } from 'astro/assets/utils';
const testImageURL = 'https://example.com/images/test.jpg';const domains = ['example.com', 'anotherdomain.com'];const remotePatterns = [  { protocol: 'https', hostname: 'images.example.com', pathname: '/**' }, // 允许该主机名下的任何路径];
const url = new URL(testImageURL);const isAllowed = isRemoteAllowed(url.href, { domains, remotePatterns });
console.log(`该远程图像可以被允许吗?${isAllowed}`);matchHostname()
Section titled “matchHostname()”类型: (url: URL, hostname?: string, allowWildcard = false): boolean
astro@4.0.0
	
	
将给定 URL 的主机名与指定的主机名进行匹配,并为通配符规则提供可选的支持。
import { matchHostname } from 'astro/assets/utils';
const testURL = new URL('https://sub.example.com/path/to/resource');
// matchHostname 函数的用法示例const hostnameToMatch = 'example.com';
// 不带有通配符进行匹配const isMatchWithoutWildcard = matchHostname(testURL, hostnameToMatch);console.log(`该主机名在不带有通配符时匹配吗?${isMatchWithoutWildcard}`); // 输出:false
// 带有通配符进行匹配const isMatchWithWildcard = matchHostname(testURL, hostnameToMatch, true);console.log(`该主机名在带有通配符时匹配吗?${isMatchWithWildcard}`); // 输出:truematchPathname()
Section titled “matchPathname()”类型: (url: URL, pathname?: string, allowWildcard = false): boolean
astro@4.0.0
	
	
将给定 URL 的路径名与指定的规则(pattern)进行匹配,并为通配符规则提供可选的支持。
import { matchPathname } from 'astro/assets/utils';
const testURL = new URL('https://example.com/images/photo.jpg');
// 用于匹配的路径名示例const pathnameToMatch = '/images/photo.jpg';
// 不带有通配符进行匹配const isMatchWithoutWildcard = matchPathname(testURL, pathnameToMatch);console.log(`该路径名在不带有通配符时匹配吗?${isMatchWithoutWildcard}`); // 输出:true
// 带有通配符进行匹配const wildcardPathname = '/images/*';const isMatchWithWildcard = matchPathname(testURL, wildcardPathname, true);console.log(`该路径名在带有通配符时匹配吗?${isMatchWithWildcard}`); // 输出:truematchPattern()
Section titled “matchPattern()”Type: (url: URL, remotePattern: RemotePattern): boolean
astro@4.0.0
	
	
评估给定的 URL 是否匹配指定的远程规则(基于协议、主机名、端口以及路径名)。
import { matchPattern } from 'astro/assets/utils';
const testURL = new URL('https://images.example.com/photos/test.jpg');
// 定义一个用于匹配 URL 的远程规则const remotePattern = {  protocol: 'https',  hostname: 'images.example.com',  pathname: '/photos/**', // 使用通配符以允许 /photos/ 下的所有文件  port: '', // 可选:匹配任意端口,留空则为默认};
// 验证该 URL 是否匹配远程规则const isPatternMatched = matchPattern(testURL, remotePattern);
console.log(`该 URL 和远程规则匹配吗?${isPatternMatched}`); // 输出:truematchPort()
Section titled “matchPort()”类型: (url: URL, port?: string): boolean
astro@4.0.0
	
	
验证给定的 URL 端口是否匹配指定的端口。如果不提供端口,则返回 true。
import { matchPort } from 'astro/assets/utils';
const testURL1 = new URL('https://example.com:8080/resource');const testURL2 = new URL('https://example.com/resource');
// matchPort 函数的用法示例const portToMatch = '8080';
// 将 URL 与指定的端口进行匹配const isPortMatch1 = matchPort(testURL1, portToMatch);console.log(`与端口匹配吗?${isPortMatch1}`); // 输出:true
// 将 URL 与未指定的端口进行匹配(将假定为默认端口)const isPortMatch2 = matchPort(testURL2, portToMatch);console.log(`与端口匹配吗?${isPortMatch2}`); // 输出:false
// 在不明确提供端口的情况下检查 URL(如果端口为 undefined,则默认为 true)const isPortMatch3 = matchPort(testURL1);console.log(`与端口匹配吗(无指定端口)?${isPortMatch3}`); // 输出:truematchProtocol()
Section titled “matchProtocol()”类型: (url: URL, protocol?: string): boolean
astro@4.0.0
	
	
将提供的 URL 所采用的协议与指定的协议进行比较。
import { matchProtocol } from 'astro/assets/utils';
const testURL1 = new URL('https://example.com/resource');const testURL2 = new URL('http://example.com/resource');
// matchProtocol 函数的用法示例const protocolToMatch = 'https';
// 将 URL 和正确的协议进行匹配const isProtocolMatch1 = matchProtocol(testURL1, protocolToMatch);console.log(`该协议与 testURL1 匹配吗?${isProtocolMatch1}`); // 输出:true
// 将 URL 和错误的协议进行匹配const isProtocolMatch2 = matchProtocol(testURL2, protocolToMatch);console.log(`该协议与 testURL2 匹配吗?${isProtocolMatch2}`); // 输出:false
// 在不明确提供协议的情况下检查 URL(如果协议为 undefined,则默认为 true)const isProtocolMatch3 = matchProtocol(testURL1);console.log(`与该协议匹配吗(无指定协议)?${isProtocolMatch3}`); // 输出:trueisESMImportedImage()
Section titled “isESMImportedImage()”类型: (src: ImageMetadata | string): boolean
astro@4.0.0
	
	
判断给定的图像来源是否为通过 ECMAScript 模块(ESM)导入的图像。
import { isESMImportedImage } from 'astro/assets/utils';
// isESMImportedImage 函数的用法示例const imageMetadataExample = {  src: '/images/photo.jpg',  width: 800,  height: 600,  format: 'jpg',};
const filePathExample = '/images/photo.jpg';
// 检查所置入的图像是否为 ESM 导入const isMetadataImage = isESMImportedImage(imageMetadataExample);console.log(`imageMetadataExample 是否为 ESM 所导入的图片?${isMetadataImage}`); // 输出:true
const isFilePathImage = isESMImportedImage(filePathExample);console.log(`isFilePathImage 是否为 ESM 所导入的图片?${isFilePathImage}`); // 输出:falseisRemoteImage()
Section titled “isRemoteImage()”类型: (src: ImageMetadata | string): boolean
astro@4.0.0
	
	
判断给定的图像来源是否为字符串类型的远程图像 URL。
import { isRemoteImage } from 'astro/assets/utils';
// isRemoteImage 函数的用法示例const remoteImageUrl = 'https://example.com/images/photo.jpg';const localImageMetadata = {  src: '/images/photo.jpg',  width: 800,  height: 600,  format: 'jpg',};
// 检查是否为远程图像 URLconst isRemote1 = isRemoteImage(remoteImageUrl);console.log(`remoteImageUrl 是远程图像吗?${isRemote1}`); // 输出:true
const isRemote2 = isRemoteImage(localImageMetadata);console.log(`localImageMetadata 是远程图像吗?${isRemote2}`); // 输出:falseresolveSrc()
Section titled “resolveSrc()”类型: (src: UnresolvedImageTransform['src']): Promise<string | ImageMetadata>
astro@4.0.0
	
	
返回图像来源。该函数确保:如果 src 是 Promise(例如:通过动态 import() 导入的模块),则会等待其解析并获取最终的 src 值;如果 src 已经是已解析的值,则直接返回。
import { resolveSrc } from 'astro/assets/utils';import localImage from "./images/photo.jpg";
const resolvedLocal = await resolveSrc(localImage);// 将会是 `{ src: '/images/photo.jpg', width: 800, height: 600, format: 'jpg' }`
const resolvedRemote = await resolveSrc("https://example.com/remote-img.jpg");// 将会是 `"https://example.com/remote-img.jpg"`
const resolvedDynamic = await resolveSrc(import("./images/dynamic-image.jpg"))// 将会是 `{ src: '/images/dynamic-image.jpg', width: 800, height: 600, format: 'jpg' }`imageMetadata()
Section titled “imageMetadata()”类型: (data: Uint8Array, src?: string): Promise<Omit<ImageMetadata, 'src' | 'fsPath'>>
astro@4.0.0
	
	
从所提供的的图像提取其元数据,诸如尺寸,格式,方向。
import { imageMetadata } from 'astro/assets/utils';
async function extractImageMetadata() {  // 图像数据示例(Uint8Array)  const exampleImageData = new Uint8Array([/* ...二进制图像数据... */]);
  // 可选来源路径(可用于调试或其他元数据上下文)  const sourcePath = '/images/photo.jpg';
  try {    // 从图像数据提取元数据    const metadata = await imageMetadata(exampleImageData, sourcePath);
    console.log('提取图像元数据', metadata);    // 输出示例:    // {    //   width: 800,    //   height: 600,    //   format: 'jpg',    //   orientation: undefined    // }  } catch (error) {    console.error('从图像提取其元数据失败:', error);  }}
await extractImageMetadata();emitESMImage()
Section titled “emitESMImage()”使用 emitImageMetadata 函数进行替代。
类型: (id: string | undefined, _watchMode: boolean, experimentalSvgEnabled: boolean,  fileEmitter?: FileEmitter): Promise<ImageMetadataWithContents | undefined>
astro@4.0.0
	
	
处理图像文件并生成其元数据及可选内容。在构建模式下,该函数使用 fileEmitter 以生成资源引用;在开发模式下,函数解析为本地文件 URL,并在 URL 中添加元数据相关的查询参数。
import { emitESMImage } from 'astro/assets/utils';
const imageId = '/images/photo.jpg';const unusedWatchMode = false; // 已弃用, 闲置const unusedExperimentalSvgEnabled = false; // 仅在你使用 SVG 并希望将文件数据嵌入时,设置为 `true`
try {  const result = await emitESMImage(imageId, unusedWatchMode, unusedExperimentalSvgEnabled);  if (result) {    console.log('图像元数据及其他内容:', result);    // 输出示例:    // {    //   width: 800,    //   height: 600,    //   format: 'jpg',    //   contents: Uint8Array([...])    // }  } else {    console.log('该图像未生成元数据。');  }} catch (error) {  console.error('生成 ESM 图像失败:', error);}emitImageMetadata()
Section titled “emitImageMetadata()”类型: (id: string | undefined, fileEmitter?: FileEmitter): Promise<ImageMetadataWithContents | undefined>
astro@5.7.0
	
	
处理图像文件并生成其元数据,可选择性地输出其内容:在构建模式下,该函数通过 fileEmitter 来生成资源引用。在开发模式下,函数将其解析为本地文件 URL,并在 URL 中添加元数据相关的查询参数。
import { emitImageMetadata } from 'astro/assets/utils';
const imageId = '/images/photo.jpg';
try {  const result = await emitImageMetadata(imageId);  if (result) {    console.log('图像元数据及内容', result);    // Example output:    // {    //   width: 800,    //   height: 600,    //   format: 'jpg',    //   contents: Uint8Array([...])    // }  } else {    console.log('该图像没有生成元数据。');  }} catch (error) {  console.error('无法生成 ESM 图像:', error);}getOrigQueryParams()
Section titled “getOrigQueryParams()”类型: (params: URLSearchParams): Pick<ImageMetadata, 'width' | 'height' | 'format'> | undefined
astro@4.0.0
	
	
从 URLSearchParams 对象 获取图片的 width、height 以及 format。如果其中任意一个参数缺失或无效,该函数都会返回 undefined。
import { getOrigQueryParams } from 'astro/assets/utils';
const url = new URL('https://example.com/image.jpg?width=800&height=600&format=jpg');const queryParams = url.searchParams;
// 提取原始查询参数const origParams = getOrigQueryParams(queryParams);
if (origParams) {  console.log('原始查询参数:', origParams);  // 输出示例:  // {  //   width: 800,  //   height: 600,  //   format: 'jpg'  // }} else {  console.log('无法提取原始查询参数。');}inferRemoteSize()
Section titled “inferRemoteSize()”类型: (url: string): Promise<Omit<ImageMetadata, 'src' | 'fsPath'>>
astro@4.0.0
	
	
通过流式传输远程图像数据并逐步分析其内容,以推断出图片的尺寸。此方法会在获取到足够的元数据时停止。
import { inferRemoteSize } from 'astro/assets/utils';
async function getRemoteImageSize() {  const remoteImageUrl = 'https://example.com/image.jpg';
  try {    // 从 URL 推断出远程图像的尺寸    const imageSize = await inferRemoteSize(remoteImageUrl);
    console.log('推断出的远程图像尺寸:', imageSize);    // 输出示例:    // {    //   width: 1920,    //   height: 1080,    //   format: 'jpg'    // }  } catch (error) {    console.error('无法推断出远程图像的尺寸:', error);  }}
await getRemoteImageSize();propsToFilename()
Section titled “propsToFilename()”类型: (filePath: string, transform: ImageTransform, hash: string): string
astro@4.0.0
	
	
基于图像的源路径、转换属性和唯一的哈希值,为其生成格式化的文件名。
格式化的文件名遵循以下结构:
<prefixDirname>/<baseFilename>_<hash><outputExtension>
prefixDirname:如果图像为 ESM 导入图像,则该项为原始文件路径的目录名;其他情况下,为空字符串。baseFilename:文件名的基本名称,如果文件为data:协议的 URI,则为哈希算法转换后的短名称。hash:生成唯一的哈希字符串,用以区分已转换的文件。outputExtension:从transform.format或原始文件扩展名得出的,所需输出文件扩展名。
import { propsToFilename } from 'astro/assets/utils';
function generateTransformedFilename() {  const filePath = '/images/photo.jpg';  const transform = {    format: 'png',    src: '/images/photo.jpg'  };  const hash = 'abcd1234';
  // 根据文件路径、转换属性和哈希值,以生成转换后的文件名  const filename = propsToFilename(filePath, transform, hash);
  console.log('已生成的、转换后的文件名:', filename);  // 输出示例:'/images/photo_abcd1234.png'}
generateTransformedFilename();hashTransform()
Section titled “hashTransform()”类型: (transform: ImageTransform, imageService: string, propertiesToHash: string[]): string
astro@4.0.0
	
	
基于选定的属性和指定的 imageService,将所提供的 transform 对象转换为哈希算法后的字符串。
import { hashTransform } from 'astro/assets/utils';
function generateTransformHash() {  const transform = {    width: 800,    height: 600,    format: 'jpg',  };
  const imageService = 'astroImageService';  const propertiesToHash = ['width', 'height', 'format'];
  // 根据转换属性、图像服务和属性以生成哈希  const hash = hashTransform(transform, imageService, propertiesToHash);
  console.log('生成的转换哈希值:', hash);  // 输出示例:'d41d8cd98f00b204e9800998ecf8427e'}
generateTransformHash();