---
title: "Supabase Storage ile React'te Dosya Yükleme: Drag & Drop Rehberi"
description: "Supabase Storage ile React'te dosya yükleme nasıl yapılır? Drag & drop, boyut validasyonu, progress bar, signed URL ve RLS ile güvenli dosya yönetimi."
date: 2026-04-13
category: react-supabase
tags: ["supabase", "storage", "react", "file-upload", "drag-drop", "typescript"]
url: https://mikroerp.dev/blog/supabase-storage-react-dosya-yukleme/
---

## Supabase Storage Nedir?

Supabase Storage, S3 uyumlu bir dosya depolama servisidir. Resim, PDF, video — her türlü dosyayı yükleyebilir, RLS ile güvenlik sağlayabilirsiniz.

## Bucket Oluşturma

```sql
-- Supabase SQL Editor
INSERT INTO storage.buckets (id, name, public)
VALUES ('documents', 'documents', false);

-- Public bucket (herkes okuyabilir)
INSERT INTO storage.buckets (id, name, public)
VALUES ('avatars', 'avatars', true);
```

## RLS Politikaları

```sql
-- Kullanıcı kendi dosyalarını yükleyebilsin
CREATE POLICY "Users upload own files" ON storage.objects
  FOR INSERT
  TO authenticated
  WITH CHECK (
    bucket_id = 'documents' AND
    (storage.foldername(name))[1] = auth.uid()::text
  );

-- Kullanıcı kendi dosyalarını okuyabilsin
CREATE POLICY "Users read own files" ON storage.objects
  FOR SELECT
  TO authenticated
  USING (
    bucket_id = 'documents' AND
    (storage.foldername(name))[1] = auth.uid()::text
  );
```

## React Dosya Yükleme Bileşeni

```tsx
import { useState, useCallback } from 'react';
import { supabase } from '../lib/supabase';

interface FileUploadProps {
  bucket: string;
  folder: string;
  accept?: string;
  maxSizeMB?: number;
  onUpload: (url: string) => void;
}

export function FileUpload({ 
  bucket, folder, accept = '*', maxSizeMB = 5, onUpload 
}: FileUploadProps) {
  const [uploading, setUploading] = useState(false);
  const [progress, setProgress] = useState(0);
  const [error, setError] = useState('');
  const [dragOver, setDragOver] = useState(false);

  const uploadFile = useCallback(async (file: File) => {
    setError('');
    
    // Boyut kontrolü
    if (file.size > maxSizeMB * 1024 * 1024) {
      setError(`Dosya ${maxSizeMB}MB'dan büyük olamaz.`);
      return;
    }

    setUploading(true);
    setProgress(0);

    const filePath = `${folder}/${Date.now()}_${file.name}`;
    
    const { data, error: uploadError } = await supabase.storage
      .from(bucket)
      .upload(filePath, file, {
        cacheControl: '3600',
        upsert: false,
      });

    if (uploadError) {
      setError(uploadError.message);
      setUploading(false);
      return;
    }

    // Public URL al
    const { data: { publicUrl } } = supabase.storage
      .from(bucket)
      .getPublicUrl(data.path);

    setProgress(100);
    setUploading(false);
    onUpload(publicUrl);
  }, [bucket, folder, maxSizeMB, onUpload]);

  const handleDrop = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    setDragOver(false);
    const file = e.dataTransfer.files[0];
    if (file) uploadFile(file);
  }, [uploadFile]);

  return (
    <div
      onDragOver={e => { e.preventDefault(); setDragOver(true); }}
      onDragLeave={() => setDragOver(false)}
      onDrop={handleDrop}
      style={{
        border: `2px dashed ${dragOver ? '#3b82f6' : '#d1d5db'}`,
        borderRadius: '12px',
        padding: '2em',
        textAlign: 'center',
        transition: 'border-color 0.2s',
        backgroundColor: dragOver ? '#eff6ff' : 'transparent',
      }}
    >
      <input
        type="file"
        accept={accept}
        onChange={e => e.target.files?.[0] && uploadFile(e.target.files[0])}
        style={{ display: 'none' }}
        id="file-input"
      />
      <label htmlFor="file-input" style={{ cursor: 'pointer' }}>
        {uploading ? (
          <p>Yükleniyor... {progress}%</p>
        ) : (
          <p>Dosyayı sürükleyin veya <strong>tıklayarak seçin</strong></p>
        )}
      </label>
      {error && <p style={{ color: 'red' }}>{error}</p>}
    </div>
  );
}
```

## Signed URL ile Geçici Erişim

Private bucket'taki dosyalara süreli erişim:

```typescript
// 1 saatlik erişim URL'i oluştur
const { data, error } = await supabase.storage
  .from('documents')
  .createSignedUrl('user123/report.pdf', 3600);

// data.signedUrl → 1 saat geçerli URL
```

## Dosya Silme

```typescript
const { error } = await supabase.storage
  .from('documents')
  .remove(['user123/old-file.pdf']);
```

## Dosya Tip ve Boyut Validasyonu

```typescript
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
const MAX_SIZE = 10 * 1024 * 1024; // 10MB

function validateFile(file: File): string | null {
  if (!ALLOWED_TYPES.includes(file.type)) {
    return `Desteklenmeyen dosya tipi: ${file.type}`;
  }
  if (file.size > MAX_SIZE) {
    return `Dosya çok büyük: ${(file.size / 1024 / 1024).toFixed(1)}MB (max: 10MB)`;
  }
  return null;
}
```

## İlgili Yazılar

- [Supabase Auth](/blog/supabase-auth-react-kullanici-yonetimi/) — Auth + Storage entegrasyonu
- [Supabase RLS](/blog/supabase-rls-cok-katmanli-yetkilendirme/) — Dosya erişim politikaları
- [.env Güvenli Yönetimi](/blog/env-dosyalari-guvenli-yonetim/) — Storage key güvenliği

---

*Bu rehber [Supabase Storage Docs](https://supabase.com/docs/guides/storage) dokümantasyonuna dayanarak hazırlanmıştır.*