婚礼回忆:合作婚礼相册!婚礼回忆

2025-06-08

婚礼回忆:合作婚礼相册!

婚礼回忆

婚礼回忆

这是Pinata 挑战赛的参赛作品

我建造了什么

Wedding Memories是一款旨在捕捉、分享和珍藏您特别日子里每一个难忘瞬间的应用!它允许婚礼宾客轻松上传照片,创建协作相册,捕捉婚礼的精髓。通过使用Pinata 的文件 API,该应用可提供安全高效的媒体上传和存储体验。

演示

点击此处查看该应用程序的实时版本:婚礼回忆

截图:

  • 上传部分- 用于选择和上传媒体文件的用户友好界面:

图片描述

  • 画廊视图- 以响应式布局显示所有已上传的回忆:

图片描述

图片描述

  • 移动优化设计- 确保所有设备上的流畅用户体验:

图片描述

  • 下载选项- 客人可以下载自己最喜欢的时刻以永远保留回忆。

图片描述

我的代码

您可以在 GitHub 上探索该项目的完整代码:

在 GitHub 上查看

更多详情

Wedding Memories 利用 Pinata 的 Files API 构建安全高效的文件上传功能。以下是集成的简要说明:

  • 文件上传:宾客可直接从设备轻松上传图片。此操作通过 实现pinata.upload.file,它使用基于 JWT 的身份验证来确保文件处理的安全。上传后,文件将被安全存储,并生成其唯一标识符 (CID) 以供检索。

  • 上传前本地预览:为了提升宾客体验,婚礼回忆会在上传照片前生成本地预览。此功能允许宾客确认选择并进行调整,确保只提交他们想要的照片。

上传成功后,文件将通过文件 API进行渲染。这确保了内容的快速访问,为访客在查看共享记忆时提供无缝体验。

  • 下载功能:宾客现在可以直接从应用程序下载自己喜爱的照片,轻松将珍贵的回忆保存到设备中。该应用程序包含一个API路由,方便安全地下载文件。

API 集成细分:

该建筑包括几个关键的 API 路线,每个路线旨在处理特定的功能:

  • 文件上传端点

它接受文件上传并使用 Pinata 的上传功能进行存储。然后,它会生成一个签名的 URL,以便轻松访问上传的文件。

import { NextResponse, NextRequest } from "next/server";
import { pinata } from "../../../../utils/config";


export async function POST(request: NextRequest) {
  try {
    const data = await request.formData();
    const file: File | null = data.get("file") as unknown as File;
    const uploadData = await pinata.upload.file(file)
    const url = await pinata.gateways.createSignedURL({
        cid: uploadData.cid,
        expires: 3600,
    });
    return NextResponse.json(url, { status: 200 });
  } catch (e) {
    console.log(e);
    return NextResponse.json(
      { error: "Internal Server Error" },
      { status: 500 }
    );
  }
}

Enter fullscreen mode Exit fullscreen mode
  • API 密钥生成(/api/key/route.ts)

创建一个临时 API 密钥,并赋予其将文件固定到 IPFS 的权限。


/* eslint-disable @typescript-eslint/no-unused-vars */
import { NextResponse } from "next/server";
import { pinata } from "../../../../utils/config";

export const dynamic = "force-dynamic";

export async function GET() {
    try {
        const uuid = crypto.randomUUID();
        const keyData = await pinata.keys.create({
            keyName: uuid.toString(),
            permissions: {
                endpoints: {
                    pinning: {
                        pinFileToIPFS: true,
                    },
                },
            },
            maxUses: 1,
        })
        return NextResponse.json(keyData, { status: 200 });
    } catch (error) {
        console.log(error);
        return NextResponse.json({ text: "Error creating API Key:" }, { status: 500 });
    }
}

Enter fullscreen mode Exit fullscreen mode
  • 列出文件(/api/listfiles/route.ts)

从 Pinata 检索已上传文件的列表,允许用户查看图库中的所有共享内容

import { NextResponse } from "next/server";
import { env } from "process";


export const dynamic = "force-dynamic";

export async function GET() {
  try {
    const options: RequestInit = {
      method: 'GET',
      headers: {
          Authorization: `Bearer ${process.env.NEXT_PUBLIC_PINATA_JWT}`,
      },
      cache: 'no-cache'
    };

    const response = await fetch('https://api.pinata.cloud/v3/files', options);

    if (!response.ok) {
      return NextResponse.json({ text: "Error listing files" }, { status: response.status });
    }

    const { data } = await response.json();
    return NextResponse.json(data, { status: 200 });
  } catch (error) {
    console.log(error, "Error listing files");
    return NextResponse.json({ text: "Error listing files" }, { status: 500 });
  }
}

Enter fullscreen mode Exit fullscreen mode
  • 图像代理(/api/proxy/route.ts)

充当从外部来源获取图像的代理,确保用户可以轻松访问和下载他们的图像。

import { NextResponse } from 'next/server';


export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const imageUrl = searchParams.get('url'); // Get the image URL from the query string

  if (!imageUrl) {
    return NextResponse.json({ error: 'Image URL is required' }, { status: 400 });
  }

  try {
    const response = await fetch(imageUrl);

    if (!response.ok) {
      return NextResponse.json({ error: 'Failed to fetch image' }, { status: response.status });
    }

    const contentType = response.headers.get('content-type') || 'application/octet-stream';
    const imageBuffer = await response.arrayBuffer();

    return new NextResponse(imageBuffer, {
      headers: {
        'Content-Type': contentType,
        'Content-Disposition': 'attachment; filename="downloaded_image"',
      },
    });
  } catch (error) {
    console.error('Error fetching image:', error);
    return NextResponse.json({ error: 'Error fetching image' }, { status: 500 });
  }
}

Enter fullscreen mode Exit fullscreen mode
  • 签名 URL 创建(/api/sign/route.ts)

为上传的文件生成签名的 URL。


import { type NextRequest, NextResponse } from "next/server";
import { pinata } from "../../../../utils/config";

export const dynamic = "force-dynamic";

export async function POST(req: NextRequest) {
  try {
    const data = await req.json();
    const mimeType = data.mime_type;

    let url;

    if (mimeType === 'video/mp4') {
      url = await pinata.gateways.createSignedURL({
        cid: data.cid,
        expires: 7776000,
      })
    } else {
      url = await pinata.gateways.createSignedURL({
        cid: data.cid,
        expires: 7776000,
      }).optimizeImage({
        width: 300,
        height: 300,
        format: "webp",
        fit: "contain",
        quality: 90,
        dpr: 2,
        sharpen: 1,

      });
    }

    return NextResponse.json(url, { status: 200 });
  } catch (error) {
    console.log(error);
    return NextResponse.json({ text: "Error creating signed URL:" }, { status: 500 });
  }
}

Enter fullscreen mode Exit fullscreen mode

该应用程序采用以下方式构建:

  • Pinata:文件 API

  • 前端:React、Next.js、Framer Motion

  • 样式:Tailwind CSS 用于响应式、美观的布局。

  • 托管:放大部署。

结论

婚礼是充满爱意、欢笑和无数难忘瞬间的欢乐时刻。捕捉这些珍贵的回忆至关重要,有了婚礼回忆 (Wedding Memories)应用,记录婚礼瞬间变得前所未有的轻松!

该应用程序允许每位嘉宾在活动当天拍摄可爱的照片并无缝分享,确保不会错过任何瞬间,让每个人都能享受庆祝活动的集体回忆。

该应用程序利用Pinata 文件 API的强大功能,为客人提供一种安全高效的方式来上传和分享他们的照片和视频,从而创建一个完美概括当天精髓的协作相册。

有关该应用程序如何与 Pinata 集成以及探索其功能的更多详细信息,请参阅Pinata 文档

谢谢。


鏂囩珷鏉ユ簮锛�https://dev.to/femi_akinyemi/wedding-memories-the-collaborative-wedding-album-2em3
PREV
React 中的客户端和服务器端数据获取
NEXT
Next.js 图像组件的响应式修复