跳到内容

如何在 Render 上配置 PHP/Node.js 文件上传 | Disk·环境变量·S3 集成

分类:Render·部署配置
本文目前仅提供日文版本。我们正在进行翻译工作。

Render 是一个作为 Heroku 替代品的流行云平台。与 Vercel 或 Netlify 不同,它使用持久进程(Web Service)模型而不是无服务器,这改变了文件上传的架构。本文介绍了 Render 的持久存储(Disk)配置、在 PHP 环境中修改 <code>upload_max_filesize</code> 的方法、使用环境变量的 S3 设置,以及基础设施即代码(IaC)中 <code>render.yaml</code>(Blueprint)的配置示例。

Client Render Web Service App (always-on) Disk (persistent) /var/data — single instance No body size limit (set in app/proxy) S3 / R2 (recommended) scale-out friendly
图:Render Web Service 与上传存储目标(Disk vs S3)

什么是 Render Disk(永久存储)?

Render 的 Web Service 默认是短暂的(重新部署时文件系统会重置)。要保持上传的文件,请添加 Disk。

存储类型 持久性 跨多个实例共享 费用
临时磁盘(默认) 重新部署时重置 不可能 免费
Render Disk(永久) 永久(保留至手动删除) 不可能(仅限单个实例) $0.25/GB/月
AWS S3(外部) 永久 支持 按 S3 定价

Render Disk 不支持扩展(多个实例),因此生产环境中的文件上传建议使用 S3 集成。Disk 适用于开发、测试环境或单实例部署。

Render Dashboard 中的 Disk 配置

可以从 Render Dashboard 的 Web Service 设置中添加磁盘。

# render.yaml(Blueprint)での Disk 設定
services:
  - type: web
    name: my-app
    runtime: node
    buildCommand: npm install && npm run build
    startCommand: npm start
    disk:
      name: uploads-disk
      mountPath: /app/uploads  # アプリ内でのマウントパス
      sizeGB: 10               # 10GB のディスク
    envVars:
      - key: UPLOAD_DIR
        value: /app/uploads
// Node.js での Disk へのファイル保存
const path = require('path');
const fs = require('fs');

const UPLOAD_DIR = process.env.UPLOAD_DIR || path.join(__dirname, 'uploads');

// アップロードディレクトリが存在しない場合は作成
if (!fs.existsSync(UPLOAD_DIR)) {
  fs.mkdirSync(UPLOAD_DIR, { recursive: true });
}

// multer の設定例(Disk ストレージ)
const multer = require('multer');

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, UPLOAD_DIR);
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1e9)}`;
    const ext = path.extname(file.originalname);
    cb(null, `upload-${uniqueSuffix}${ext}`);
  },
});

const upload = multer({
  storage,
  limits: { fileSize: 50 * 1024 * 1024 }, // 50MB
  fileFilter: (req, file, cb) => {
    const allowedTypes = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
    if (allowedTypes.includes(file.mimetype)) {
      cb(null, true);
    } else {
      cb(new Error('許可されていないファイル形式です。'));
    }
  },
});

PHP环境中的upload_max_filesize配置

对于 Render 上的 PHP 应用(基于 Docker),您可以通过修改 <code>php.ini</code> 设置来调整上传大小。Render 支持任意 Docker 配置,比其他托管服务更灵活。

# Dockerfile(PHP アプリ)
FROM php:8.2-fpm

# 必要な拡張をインストール
RUN docker-php-ext-install pdo pdo_mysql

# php.ini の設定をオーバーライド
RUN echo "upload_max_filesize = 100M" > /usr/local/etc/php/conf.d/uploads.ini \
 && echo "post_max_size = 110M" >> /usr/local/etc/php/conf.d/uploads.ini \
 && echo "memory_limit = 256M" >> /usr/local/etc/php/conf.d/uploads.ini \
 && echo "max_execution_time = 120" >> /usr/local/etc/php/conf.d/uploads.ini \
 && echo "max_input_time = 120" >> /usr/local/etc/php/conf.d/uploads.ini

WORKDIR /var/www/html
COPY . .

EXPOSE 9000
CMD ["php-fpm"]
# php.ini(直接配置する場合)
; ファイルアップロードの有効化
file_uploads = On

; 1ファイルあたりの上限
upload_max_filesize = 100M

; POST データ全体の上限(upload_max_filesize より大きくする)
post_max_size = 110M

; PHP スクリプトのメモリ上限
memory_limit = 256M

; スクリプトの最大実行時間(秒)
max_execution_time = 120

; ファイル入力待ちの最大時間(秒)
max_input_time = 120

; 一時ファイルのディレクトリ(Render の場合は /tmp を使用)
upload_tmp_dir = /tmp
<?php
// .htaccess が使えない場合は ini_set() で実行時に変更(共有ホスティングでは制限あり)
// Render(Docker)では Dockerfile での設定が確実

// ini_set() による実行時オーバーライド(一部の設定のみ有効)
ini_set('memory_limit', '256M');
// upload_max_filesize と post_max_size は実行時変更不可(php.ini での設定が必要)

// 設定値の確認
echo ini_get('upload_max_filesize'); // "100M"
echo ini_get('post_max_size');       // "110M"
echo ini_get('memory_limit');        // "256M"

使用环境变量的S3配置

在 Render Dashboard 或 <code>render.yaml</code> 中设置环境变量,从您的应用访问 AWS S3。使用 Render 的秘密管理功能处理敏感信息,<code>render.yaml</code> 中仅包含密钥。

# render.yaml(Blueprint)— シークレットはキーのみ定義
services:
  - type: web
    name: my-php-app
    runtime: docker
    dockerfilePath: ./Dockerfile
    envVars:
      # 非シークレット(値を直接記載)
      - key: AWS_REGION
        value: ap-northeast-1
      - key: AWS_S3_BUCKET
        value: my-app-uploads

      # シークレット(Render Dashboard で値を設定)
      - key: AWS_ACCESS_KEY_ID
        sync: false          # sync: false でダッシュボードで手動入力
      - key: AWS_SECRET_ACCESS_KEY
        sync: false

      # または Render の Environment Group を参照
      - fromGroup: aws-credentials

    disk:
      name: tmp-uploads
      mountPath: /tmp/uploads
      sizeGB: 5
<?php
// PHP での AWS SDK を使った S3 アップロード
require 'vendor/autoload.php';

use Aws\S3\S3Client;
use Aws\Exception\AwsException;

$s3 = new S3Client([
    'version' => 'latest',
    'region'  => getenv('AWS_REGION'),
    'credentials' => [
        'key'    => getenv('AWS_ACCESS_KEY_ID'),
        'secret' => getenv('AWS_SECRET_ACCESS_KEY'),
    ],
]);

function uploadToS3(array $file, string $bucket): array
{
    global $s3;

    $key = 'uploads/' . uniqid('file_', true) . '_' . basename($file['name']);

    try {
        $result = $s3->putObject([
            'Bucket'      => $bucket,
            'Key'         => $key,
            'SourceFile'  => $file['tmp_name'],
            'ContentType' => mime_content_type($file['tmp_name']),
            'ACL'         => 'private',
        ]);

        return [
            'success' => true,
            'key'     => $key,
            'url'     => $result['ObjectURL'],
        ];
    } catch (AwsException $e) {
        return ['success' => false, 'error' => $e->getMessage()];
    }
}

// アップロード処理
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
    $file = $_FILES['file'];

    // バリデーション
    $maxSize = 50 * 1024 * 1024; // 50MB
    if ($file['size'] > $maxSize) {
        http_response_code(413);
        echo json_encode(['error' => 'ファイルが大きすぎます(上限50MB)。']);
        exit;
    }

    $allowedMimes = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
    $detectedMime = mime_content_type($file['tmp_name']); // サーバー側でMIMEを検出
    if (!in_array($detectedMime, $allowedMimes)) {
        http_response_code(415);
        echo json_encode(['error' => '許可されていないファイル形式です。']);
        exit;
    }

    $result = uploadToS3($file, getenv('AWS_S3_BUCKET'));
    echo json_encode($result);
}

render.yaml(Blueprint)中的完整配置示例

使用 Blueprint,你可以将 Render 基础设施作为代码进行管理。你可以一起定义 Web Service、数据库、Redis、Disk 和环境变量。

# render.yaml
version: "1"

services:
  # Web アプリケーション
  - type: web
    name: file-upload-app
    runtime: node
    plan: starter           # free / starter / standard / pro
    region: singapore       # oregon / frankfurt / singapore / ohio
    buildCommand: npm ci && npm run build
    startCommand: node server.js
    healthCheckPath: /health
    autoDeploy: true        # Git push で自動デプロイ

    # 永続ストレージ(スケールアウト不可)
    disk:
      name: user-uploads
      mountPath: /app/data/uploads
      sizeGB: 20

    # 環境変数
    envVars:
      - key: NODE_ENV
        value: production
      - key: PORT
        value: 3000
      - key: UPLOAD_DIR
        value: /app/data/uploads
      - key: AWS_REGION
        value: ap-northeast-1
      - key: AWS_S3_BUCKET
        value: my-production-bucket

      # シークレット(ダッシュボードで値を設定)
      - key: AWS_ACCESS_KEY_ID
        sync: false
      - key: AWS_SECRET_ACCESS_KEY
        sync: false
      - key: DATABASE_URL
        fromDatabase:
          name: app-db
          property: connectionString

  # PostgreSQL データベース
  - type: pserv
    name: app-db
    runtime: postgres
    plan: starter
    region: singapore
    databaseName: app_production

本文中可用的测试文件(免费)

  • → <a href="/ja/files/images/png/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">PNG 测试图像(1MB)</a> — 用于验证到 Render Disk·S3 的上传行为
  • → <a href="/ja/files/pdf/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">PDF 测试文件(1MB)</a> — 用于验证 PHP 的 <code>upload_max_filesize</code> 配置
  • → <a href="/ja/files/zip/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">ZIP 测试文件(1MB)</a> — 用于边界值测试和 S3 直接上传验证

相关文章

  • → <a href="/ja/blog/vercel-upload-config/" class="text-primary-600 dark:text-primary-400 hover:underline">Vercel 文件上传配置和限制 | Vercel Blob・API 限制・解决方案</a>
  • → <a href="/ja/blog/netlify-upload-config/" class="text-primary-600 dark:text-primary-400 hover:underline">使用 Netlify Functions 上传文件的方法|限制、Large Media 和解决方案</a>
  • → <a href="/ja/blog/laravel-file-upload/" class="text-primary-600 dark:text-primary-400 hover:underline">Laravel 文件上传实现指南|验证、Storage 和 S3 支持</a>