Transform user-uploaded images before up·续

export default { async fetch(request, env) { if (request.method === "GET") { return new Response(html, { headers: { "Content-Type": "text/html" } }); } if (request.method === "POST") { try { // Parse form data const formData = await request.formData(); const file = formData.get("image"); if (!file || typeof file.stream !== "function") { return new Response("No image file provided", { status: 400 }); }

// Get uploaded image as a readable stream const fileStream = file.stream();

// Fetch image as watermark const watermarkResponse = await env.ASSETS.fetch( assetUrl(request, "watermark.png"), ); const watermarkStream = watermarkResponse.body; if (!watermarkStream) { return new Response("Failed to fetch watermark", { status: 500 }); }

// Apply watermark and convert to AVIF const imageResponse = ( await env.IMAGES.input(fileStream) // Draw the watermark on top of the image .draw( env.IMAGES.input(watermarkStream).transform({ width: 100, height: 100, }), { bottom: 10, right: 10, opacity: 0.75 }, ) // Output the final image as AVIF .output({ format: "image/avif" }) ).response(); } catch (err) { console.log(err.message); } } }, };


```ts
function assetUrl(request: Request, path: string): URL {
    const url = new URL(request.url);
    url.pathname = path;
    return url;
}

export default {
    async fetch(request: Request, env: Env): Promise<Response> {
        if (request.method === "GET") {
            return new Response(html, { headers: { "Content-Type": "text/html" } });
        }
        if (request.method === "POST") {
            try {
                // Parse form data
                const formData = await request.formData();
                const file = formData.get("image");
                if (!file || typeof file.stream !== "function") {
                    return new Response("No image file provided", { status: 400 });
                }

// Get uploaded image as a readable stream
                const fileStream = file.stream();

// Fetch image as watermark
                const watermarkResponse = await env.ASSETS.fetch(
                    assetUrl(request, "watermark.png"),
                );
                const watermarkStream = watermarkResponse.body;
                if (!watermarkStream) {
                    return new Response("Failed to fetch watermark", { status: 500 });
                }

// Apply watermark and convert to AVIF
                const imageResponse = (
                    await env.IMAGES.input(fileStream)
                        // Draw the watermark on top of the image
                        .draw(
                            env.IMAGES.input(watermarkStream).transform({
                                width: 100,
                                height: 100,
                            }),
                            { bottom: 10, right: 10, opacity: 0.75 },
                        )
                        // Output the final image as AVIF
                        .output({ format: "image/avif" })
                ).response();
            } catch (err) {
                console.log((err as Error).message);
            }
        }
    },
} satisfies ExportedHandler<Env>;

5: Upload to R2

Upload the transformed image to R2.

By creating a fileName variable, you can specify the name of the transformed image. In this example, you append the date to the name of the original image before uploading to R2.

Here is the full code for the example:

const html = `
<!DOCTYPE html>
        <html>
          <head>
            <meta charset="UTF-8">
            <title>Upload Image</title>
          </head>
          <body>
            <h1>Upload an image</h1>
            <form method="POST" enctype="multipart/form-data">
              <input type="file" name="image" accept="image/*" required />
              <button type="submit">Upload</button>
            </form>
          </body>
        </html>
`;

function assetUrl(request, path) {
    const url = new URL(request.url);
    url.pathname = path;
    return url;
}

export default {
    async fetch(request, env) {
        if (request.method === "GET") {
            return new Response(html, { headers: { "Content-Type": "text/html" } });
        }
        if (request.method === "POST") {
            try {
                // Parse form data
                const formData = await request.formData();
                const file = formData.get("image");
                if (!file || typeof file.stream !== "function") {
                    return new Response("No image file provided", { status: 400 });
                }

// Get uploaded image as a readable stream
                const fileStream = file.stream();

// Fetch image as watermark
                const watermarkResponse = await env.ASSETS.fetch(
                    assetUrl(request, "watermark.png"),
                );
                const watermarkStream = watermarkResponse.body;
                if (!watermarkStream) {
                    return new Response("Failed to fetch watermark", { status: 500 });
                }

// Apply watermark and convert to AVIF
                const imageResponse = (
                    await env.IMAGES.input(fileStream)
                        // Draw the watermark on top of the image
                        .draw(
                            env.IMAGES.input(watermarkStream).transform({
                                width: 100,
                                height: 100,
                            }),
                            { bottom: 10, right: 10, opacity: 0.75 },
                        )
                        // Output the final image as AVIF
                        .output({ format: "image/avif" })
                ).response();

// Add timestamp to file name
                const fileName = `image-${Date.now()}.avif`;

// Upload to R2
                await env.R2.put(fileName, imageResponse.body);

return new Response(`Image uploaded successfully as ${fileName}`, {
                    status: 200,
                });
            } catch (err) {
                console.log(err.message);
                return new Response("Internal error", { status: 500 });
            }
        }
        return new Response("Method not allowed", { status: 405 });
    },
};
interface Env {
    IMAGES: ImagesBinding;
    R2: R2Bucket;
    ASSETS: Fetcher;
}

const html = `
<!DOCTYPE html>
        <html>
          <head>
            <meta charset="UTF-8">
            <title>Upload Image</title>
          </head>
          <body>
            <h1>Upload an image</h1>
            <form method="POST" enctype="multipart/form-data">
              <input type="file" name="image" accept="image/*" required />
              <button type="submit">Upload</button>
            </form>
          </body>
        </html>
`;

function assetUrl(request: Request, path: string): URL {
    const url = new URL(request.url);
    url.pathname = path;
    return url;
}

export default {
    async fetch(request: Request, env: Env): Promise<Response> {
        if (request.method === "GET") {
            return new Response(html, { headers: { "Content-Type": "text/html" } });
        }
        if (request.method === "POST") {
            try {
                // Parse form data
                const formData = await request.formData();
                const file = formData.get("image");
                if (!file || typeof file.stream !== "function") {
                    return new Response("No image file provided", { status: 400 });
                }

// Get uploaded image as a readable stream
                const fileStream = file.stream();

// Fetch image as watermark
                const watermarkResponse = await env.ASSETS.fetch(
                    assetUrl(request, "watermark.png"),
                );
                const watermarkStream = watermarkResponse.body;
                if (!watermarkStream) {
                    return new Response("Failed to fetch watermark", { status: 500 });
                }

// Apply watermark and convert to AVIF
                const imageResponse = (
                    await env.IMAGES.input(fileStream)
                        // Draw the watermark on top of the image
                        .draw(
                            env.IMAGES.input(watermarkStream).transform({
                                width: 100,
                                height: 100,
                            }),
                            { bottom: 10, right: 10, opacity: 0.75 },
                        )
                        // Output the final image as AVIF
                        .output({ format: "image/avif" })
                ).response();

// Add timestamp to file name
                const fileName = `image-${Date.now()}.avif`;

// Upload to R2
                await env.R2.put(fileName, imageResponse.body);

return new Response(`Image uploaded successfully as ${fileName}`, {
                    status: 200,
                });
            } catch (err) {
                console.log((err as Error).message);
                return new Response("Internal error", { status: 500 });
            }
        }
        return new Response("Method not allowed", { status: 405 });
    },
} satisfies ExportedHandler<Env>;

Next steps

In this tutorial, you learned how to connect your Worker to various resources on the Developer Platform to build an app that accepts image uploads, transform images, and uploads the output to R2.

Next, you can set up a transformation URL to dynamically optimize images that are stored in R2.

Was this helpful?

YesNo

On this page

Transform user-uploaded images before up·续 - 图1Docs

{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/images/tutorials/optimize-user-uploaded-image/#page","headline":"Transform user-uploaded images before uploading to R2","description":"Set up bindings to connect Images, R2, and Assets to your Worker","url":"https://developers.cloudflare.com/images/tutorials/optimize-user-uploaded-image/","inLanguage":"en","image":"https://developers.cloudflare.com/images/tutorials/optimize-user-uploaded-image/og.png?v=624878e5657218c5","dateModified":"2026-06-10","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}


description: Code examples for common Cloudflare Images use cases, including transcoding and watermarking. title: Examples image: https://developers.cloudflare.com/images/examples/og.png?v=8d4d3c971565cdc1


Skip to content

Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/images/llms.txt
Use this file to discover all available pages before exploring further.