برنامه نویسی

ساخت یک سیستم بارگیری فایل آماده تولید با Goframe

مقدمه

سلام devs! اگر در حال ساخت برنامه های وب با GO هستید ، احتمالاً با نیاز به رسیدگی به بارگیری فایل روبرو شده اید. در حالی که ممکن است در ابتدا ساده به نظر برسد ، اجرای بارگیری پرونده های آماده تولید با مجموعه چالش های خاص خود همراه است. در این راهنما ، من بینش های عملی را در مورد ساخت قابلیت بارگیری فایل قوی با استفاده از goframe به اشتراک می گذارم.

TL ؛ DR: ما همه چیز را از بارگیری فایل های اساسی گرفته تا پیاده سازی های آماده تولید با Goframe ، از جمله رسیدگی به پرونده های بزرگ ، اجرای پشتیبانی رزومه و بهینه سازی عملکرد ، پوشش خواهیم داد.

آنچه ما پوشش خواهیم داد

  • تنظیم بارگیری فایل های اساسی
  • رسیدگی به پرونده های بزرگ بدون مشکلات حافظه
  • اجرای رزومه/بارگیری جزئی
  • افزودن اقدامات امنیتی
  • بهینه سازی برای تولید
  • نمونه های دنیای واقعی و Gotchas

چرا Goframe برای بارگیری فایل؟ 🤔

قبل از شیرجه رفتن ، ممکن است تعجب کنید که چرا Goframe را برای رسیدگی به بارگیری فایل انتخاب کنید. این معامله است: در حالی که می توانید بارگیری فایل را با کتابخانه های استاندارد GO پیاده سازی کنید ، Goframe برخی از انتزاع های خوب را فراهم می کند که زندگی ما را آسان تر می کند:

  • پردازش مبتنی بر جریان (مشکلات حافظه خداحافظ!)
  • پشتیبانی داخلی داخلی
  • رسیدگی به خطای تمیز
  • قابلیت های ردیابی پیشرفت

شروع کار

اول چیزها ابتدا ، بیایید محیط خود را تنظیم کنیم. اطمینان حاصل کنید که نصب شده اید (1.16+) ، سپس Goframe را بگیرید:

go get -u github.com/gogf/gf/v2@latest
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اجرای اصلی

بیایید با یک کنترل کننده بارگیری فایل ساده شروع کنیم. این نقطه ورود شما برای درک چگونگی بارگیری Goframe است:

package handler

import (
    "github.com/gogf/gf/v2/net/ghttp"
)

func SimpleDownload(r *ghttp.Request) {
    filePath := r.Get("file").String()

    // Quick security check
    if !isValidFile(filePath) {
        r.Response.WriteStatus(403)
        return
    }

    // Set headers and serve
    r.Response.Header().Set("Content-Type", "application/octet-stream")
    r.Response.ServeFileDownload(filePath)
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

خیلی ساده ، درست است؟ اما صبر کنید – در هنگام ساخت برای تولید بیشتر باید در نظر بگیرید! 🛠

امنیت اول! 🔒

قبل از اینکه از خدمت به پرونده ها خیلی هیجان زده شویم ، بیایید با امنیت صحبت کنیم. در اینجا یک عملکرد اعتبار سنجی قوی برای جلوگیری از مسائل امنیتی مشترک وجود دارد:

var allowedExtensions = map[string]bool{
    ".txt":  true,
    ".pdf":  true,
    ".doc":  true,
    ".xlsx": true,
}

func isValidFile(filePath string) bool {
    // 🚫 Path traversal check
    if strings.Contains(filePath, "..") {
        return false
    }

    // 📁 File existence & type check
    fileInfo, err := os.Stat(filePath)
    if err != nil || !fileInfo.Mode().IsRegular() {
        return false
    }

    // 📏 Size check (100MB limit)
    if fileInfo.Size() > 100*1024*1024 {
        return false
    }

    // 🔍 Extension check
    ext := strings.ToLower(filepath.Ext(filePath))
    return allowedExtensions[ext]
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

دست زدن به پرونده های بزرگ مانند یک حرفه ای

اکنون ، اینجاست که همه چیز جالب می شود. هنگام برخورد با پرونده های بزرگ ، نمی خواهید همه چیز را در حافظه بارگذاری کنید. در اینجا یک اجرای جریان وجود دارد که باعث می شود سرور شما گریه کند:

func StreamDownload(r *ghttp.Request) {
    const bufSize = 32 * 1024 // 32KB chunks

    file := r.Get("file").String()
    fd, err := os.Open(file)
    if err != nil {
        r.Response.WriteStatus(500)
        return
    }
    defer fd.Close() // Don't forget this! 😉

    info, _ := fd.Stat()
    r.Response.Header().Set("Content-Length", gconv.String(info.Size()))

    // Stream in chunks
    buf := make([]byte, bufSize)
    for {
        n, err := fd.Read(buf)
        if n > 0 {
            r.Response.Write(buf[:n])
        }
        if err == io.EOF {
            break
        }
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

رزومه پشتیبانی: زیرا بارگیری ها گاهی اوقات شکست می خورند

بیایید واقعی باشیم – بارگیری ها می توانند شکست بخورند ، به خصوص برای پرونده های بزرگ. در اینجا نحوه اجرای پشتیبانی رزومه ارائه شده است:

func ResumeDownload(r *ghttp.Request) {
    rangeHeader := r.Header.Get("Range")
    if rangeHeader != "" {
        // Handle range request
        // ... (previous range parsing code)

        r.Response.Header().Set("Content-Range",
            fmt.Sprintf("bytes %d-%d/%d", start, end, fileSize))
        r.Response.WriteStatus(206) // Partial Content

        // Serve the requested chunk
        streamFileContent(r, fd, end-start+1)
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

سناریوها و راه حل های دنیای واقعی

بیایید به برخی از سناریوهای متداول که ممکن است با آنها روبرو شوید و نحوه رسیدگی به آنها را بررسی کنیم:

1. رسیدگی به انواع مختلف فایل

انواع مختلف فایل ها اغلب به رسیدگی متفاوتی نیاز دارند. در اینجا یک مثال عملی وجود دارد:

func SmartDownload(r *ghttp.Request) {
    filePath := r.Get("file").String()
    fileExt := strings.ToLower(filepath.Ext(filePath))

    switch fileExt {
    case ".pdf":
        // For PDFs, we might want to allow preview
        r.Response.Header().Set("Content-Type", "application/pdf")
        r.Response.Header().Set("Content-Disposition", "inline")
    case ".csv":
        // For CSVs, we might want to add BOM for Excel compatibility
        r.Response.Header().Set("Content-Type", "text/csv")
        r.Response.Write("\xEF\xBB\xBF") // Add BOM
    case ".mp4":
        // For videos, support range requests for streaming
        handleVideoStream(r)
    default:
        // Default download behavior
        r.Response.Header().Set("Content-Type", "application/octet-stream")
        r.Response.Header().Set("Content-Disposition", "attachment")
    }

    r.Response.ServeFileDownload(filePath)
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

2. استفاده از سازگاری مرورگر

مرورگرهای مختلف بارگیری های دیگری را انجام می دهند. در اینجا نحوه رسیدگی به آن آورده شده است:

func BrowserAwareDownload(r *ghttp.Request) {
    filename := r.Get("file").String()
    userAgent := r.Header.Get("User-Agent")

    // Handle filename encoding for different browsers
    if strings.Contains(userAgent, "MSIE") || 
       strings.Contains(userAgent, "Edge") {
        // URL encode for IE/Edge
        filename = url.QueryEscape(filename)
    } else {
        // RFC 5987 encoding for others
        filename = fmt.Sprintf("UTF-8''%s", 
            url.QueryEscape(filename))
    }

    disposition := fmt.Sprintf("attachment; filename*=%s", filename)
    r.Response.Header().Set("Content-Disposition", disposition)
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

3. بارگیری نظارت بر پیشرفت

در اینجا نحوه اجرای نظارت پیشرفت با به روزرسانی های WebSocket آورده شده است:

type Progress struct {
    Total     int64   `json:"total"`
    Current   int64   `json:"current"`
    Speed     float64 `json:"speed"`
    Remaining int     `json:"remaining"`
}

func ProgressDownload(r *ghttp.Request) {
    ws, err := r.WebSocket()
    if err != nil {
        return
    }

    file := r.Get("file").String()
    info, _ := os.Stat(file)
    total := info.Size()

    // Create a custom reader that reports progress
    reader := &ProgressReader{
        Reader: bufio.NewReader(file),
        Total:  total,
        OnProgress: func(current int64) {
            progress := Progress{
                Total:     total,
                Current:   current,
                Speed:    calculateSpeed(current),
                Remaining: calculateRemaining(current, total),
            }
            ws.WriteJSON(progress)
        },
    }

    io.Copy(r.Response.Writer, reader)
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نکات تولید و بهترین شیوه ها

پس از اجرای بارگیری فایل در محیط های متعدد تولید ، در اینجا برخی از نکات آزمایش شده توسط نبرد آورده شده است:

همیشه حد مجاز 🚦

var downloadLimiter = rate.NewLimiter(rate.Limit(100), 200)
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

حافظه پنهان 🗄

func CachedDownload(r *ghttp.Request) {
    if data := memCache.Get(file); data != nil {
        return serveContent(r, data)
    }
    // ... fallback to disk
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نظارت بر همه چیز 📊

در اینجا یک اجرای نظارت عملی وجود دارد:

type DownloadMetrics struct {
    ActiveDownloads    *atomic.Int64
    TotalBytes        *atomic.Int64
    ErrorCount        *atomic.Int64
    DownloadDurations *metrics.Histogram
}

func NewDownloadMetrics() *DownloadMetrics {
    return &DownloadMetrics{
        ActiveDownloads:    atomic.NewInt64(0),
        TotalBytes:        atomic.NewInt64(0),
        ErrorCount:        atomic.NewInt64(0),
        DownloadDurations: metrics.NewHistogram(metrics.HistogramOpts{
            Buckets: []float64{.1, .5, 1, 2.5, 5, 10, 30},
        }),
    }
}

func (m *DownloadMetrics) Track(r *ghttp.Request) func() {
    start := time.Now()
    m.ActiveDownloads.Add(1)

    return func() {
        m.ActiveDownloads.Add(-1)
        duration := time.Since(start).Seconds()
        m.DownloadDurations.Observe(duration)
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اجرای قطع کننده مدار 🔌

از سیستم خود با قطع کننده مدار محافظت کنید:

type DownloadBreaker struct {
    breaker *gobreaker.CircuitBreaker
}

func NewDownloadBreaker() *DownloadBreaker {
    return &DownloadBreaker{
        breaker: gobreaker.NewCircuitBreaker(gobreaker.Settings{
            Name:        "download-breaker",
            MaxRequests: 100,
            Interval:    10 * time.Second,
            Timeout:     30 * time.Second,
            ReadyToTrip: func(counts gobreaker.Counts) bool {
                failureRatio := float64(counts.TotalFailures) / float64(counts.Requests)
                return counts.Requests >= 10 && failureRatio >= 0.6
            },
        }),
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

خاموش کردن برازنده 🛑

خاموش کردن ها را به درستی انجام دهید:

type DownloadManager struct {
    activeDownloads sync.WaitGroup
    shutdownCh     chan struct{}
}

func (dm *DownloadManager) HandleDownload(r *ghttp.Request) {
    dm.activeDownloads.Add(1)
    defer dm.activeDownloads.Done()

    // Create download context with shutdown signal
    ctx, cancel := context.WithCancel(r.Context())
    defer cancel()

    go func() {
        select {
        case <-dm.shutdownCh:
            // Save progress and cleanup
            cancel()
        case <-ctx.Done():
            return
        }
    }()

    // Proceed with download...
}

func (dm *DownloadManager) Shutdown(timeout time.Duration) error {
    close(dm.shutdownCh)

    // Wait for active downloads with timeout
    c := make(chan struct{})
    go func() {
        dm.activeDownloads.Wait()
        close(c)
    }()

    select {
    case <-c:
        return nil
    case <-time.After(timeout):
        return errors.New("shutdown timeout")
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ویژگی ها و نمونه های پیشرفته

1. بارگیری زیپ در پرواز

آیا نیاز به ایجاد بایگانی زیپ به صورت پویا دارید؟ در اینجا چگونه:

func ZipDownload(r *ghttp.Request) {
    files := r.GetArray("files")

    r.Response.Header().Set("Content-Type", "application/zip")
    r.Response.Header().Set("Content-Disposition", 
        "attachment; filename=archive.zip")

    zw := zip.NewWriter(r.Response.Writer)
    defer zw.Close()

    for _, file := range files {
        // Add file to zip
        f, err := os.Open(file)
        if err != nil {
            continue
        }
        defer f.Close()

        // Create zip entry
        header := &zip.FileHeader{
            Name:   filepath.Base(file),
            Method: zip.Deflate,
        }

        writer, err := zw.CreateHeader(header)
        if err != nil {
            continue
        }

        io.Copy(writer, f)
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

2. ادغام ذخیره سازی ابری 🌥

در اینجا مثالی با ذخیره سازگار با S3 وجود دارد:

func CloudDownload(r *ghttp.Request) {
    bucket := "my-bucket"
    key := r.Get("key").String()

    // Get object from S3
    input := &s3.GetObjectInput{
        Bucket: aws.String(bucket),
        Key:    aws.String(key),
    }

    result, err := s3Client.GetObject(input)
    if err != nil {
        r.Response.WriteStatus(500)
        return
    }
    defer result.Body.Close()

    // Set headers
    r.Response.Header().Set("Content-Type", *result.ContentType)
    r.Response.Header().Set("Content-Length", 
        fmt.Sprintf("%d", *result.ContentLength))

    // Stream the response
    io.Copy(r.Response.Writer, result.Body)
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

3. اجرای صف را بارگیری کنید

برای مدیریت تعداد زیادی بارگیری:

type DownloadQueue struct {
    queue    chan DownloadJob
    workers  int
    metrics  *DownloadMetrics
}

type DownloadJob struct {
    ID       string
    FilePath string
    Priority int
    Callback func(error)
}

func NewDownloadQueue(workers int) *DownloadQueue {
    dq := &DownloadQueue{
        queue:   make(chan DownloadJob, 1000),
        workers: workers,
        metrics: NewDownloadMetrics(),
    }

    for i := 0; i < workers; i++ {
        go dq.worker()
    }

    return dq
}

func (dq *DownloadQueue) worker() {
    for job := range dq.queue {
        // Process download job
        err := processDownload(job)
        if job.Callback != nil {
            job.Callback(err)
        }
    }
}

func (dq *DownloadQueue) Enqueue(job DownloadJob) error {
    select {
    case dq.queue <- job:
        return nil
    default:
        return errors.New("queue full")
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

الگوهای رسیدگی خطای جامع

بیایید به نحوه رسیدگی به سناریوهای مختلف خطای با قاطعیت بپردازیم:

// Custom error types for better error handling
type DownloadError struct {
    Code    int
    Message string
    Err     error
}

func (e *DownloadError) Error() string {
    if e.Err != nil {
        return fmt.Sprintf("%s: %v", e.Message, e.Err)
    }
    return e.Message
}

// Error codes
const (
    ErrFileNotFound = iota + 1000
    ErrPermissionDenied
    ErrQuotaExceeded
    ErrInvalidFileType
    ErrFileTooLarge
)

// Robust download handler with error handling
func RobustDownload(r *ghttp.Request) {
    defer func() {
        if err := recover(); err != nil {
            // Log the stack trace
            debug.PrintStack()
            // Return 500 error to client
            r.Response.WriteStatus(500)
        }
    }()

    file := r.Get("file").String()

    // Validate request
    if err := validateDownloadRequest(r); err != nil {
        handleDownloadError(r, err)
        return
    }

    // Check user quota
    if err := checkUserQuota(r); err != nil {
        handleDownloadError(r, &DownloadError{
            Code:    ErrQuotaExceeded,
            Message: "Download quota exceeded",
            Err:     err,
        })
        return
    }

    // Attempt file download
    if err := streamFileWithRetry(r, file); err != nil {
        handleDownloadError(r, err)
        return
    }
}

// Error handler for different scenarios
func handleDownloadError(r *ghttp.Request, err error) {
    var downloadErr *DownloadError
    if errors.As(err, &downloadErr) {
        switch downloadErr.Code {
        case ErrFileNotFound:
            r.Response.WriteStatus(404)
            r.Response.WriteJson(g.Map{
                "error": "File not found",
                "details": downloadErr.Message,
            })
        case ErrPermissionDenied:
            r.Response.WriteStatus(403)
            r.Response.WriteJson(g.Map{
                "error": "Access denied",
                "details": downloadErr.Message,
            })
        case ErrQuotaExceeded:
            r.Response.WriteStatus(429)
            r.Response.WriteJson(g.Map{
                "error": "Quota exceeded",
                "details": downloadErr.Message,
            })
        default:
            r.Response.WriteStatus(500)
            r.Response.WriteJson(g.Map{
                "error": "Internal server error",
                "reference": uuid.New().String(),
            })
        }
        return
    }

    // Handle generic errors
    r.Response.WriteStatus(500)
}

// Retry mechanism for transient failures
func streamFileWithRetry(r *ghttp.Request, file string) error {
    const maxRetries = 3
    const baseDelay = 100 * time.Millisecond

    var lastErr error
    for attempt := 0; attempt < maxRetries; attempt++ {
        err := streamFile(r, file)
        if err == nil {
            return nil
        }

        lastErr = err

        // Don't retry on certain errors
        if errors.Is(err, os.ErrNotExist) || 
           errors.Is(err, os.ErrPermission) {
            return err
        }

        // Exponential backoff
        delay := baseDelay * time.Duration(math.Pow(2, float64(attempt)))
        time.Sleep(delay)
    }

    return fmt.Errorf("failed after %d attempts: %w", maxRetries, lastErr)
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نمونه های مورد استفاده خاص

1. پخش ویدیو با پشتیبانی HLS

func VideoStreamHandler(r *ghttp.Request) {
    videoPath := r.Get("video").String()

    // Check if requesting manifest
    if strings.HasSuffix(videoPath, ".m3u8") {
        serveHLSManifest(r, videoPath)
        return
    }

    // Check if requesting segment
    if strings.HasSuffix(videoPath, ".ts") {
        serveHLSSegment(r, videoPath)
        return
    }

    // Serve video file directly with range support
    serveVideoWithRange(r, videoPath)
}

func serveHLSManifest(r *ghttp.Request, path string) {
    r.Response.Header().Set("Content-Type", "application/vnd.apple.mpegurl")
    r.Response.Header().Set("Cache-Control", "max-age=5")

    // Read and serve manifest
    content, err := ioutil.ReadFile(path)
    if err != nil {
        r.Response.WriteStatus(404)
        return
    }

    r.Response.Write(content)
}

func serveVideoWithRange(r *ghttp.Request, path string) {
    info, err := os.Stat(path)
    if err != nil {
        r.Response.WriteStatus(404)
        return
    }

    file, err := os.Open(path)
    if err != nil {
        r.Response.WriteStatus(500)
        return
    }
    defer file.Close()

    rangeHeader := r.Header.Get("Range")
    if rangeHeader != "" {
        ranges, err := parseRange(rangeHeader, info.Size())
        if err != nil {
            r.Response.WriteStatus(416)
            return
        }

        if len(ranges) > 0 {
            start, end := ranges[0][0], ranges[0][1]
            r.Response.Header().Set("Content-Range",
                fmt.Sprintf("bytes %d-%d/%d", start, end, info.Size()))
            r.Response.Header().Set("Content-Length",
                fmt.Sprintf("%d", end-start+1))
            r.Response.WriteStatus(206)

            file.Seek(start, 0)
            io.CopyN(r.Response.Writer, file, end-start+1)
            return
        }
    }

    r.Response.Header().Set("Content-Length", fmt.Sprintf("%d", info.Size()))
    r.Response.Header().Set("Content-Type", "video/mp4")
    io.Copy(r.Response.Writer, file)
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

2. تولید پرونده بزرگ اکسل

func ExcelDownloadHandler(r *ghttp.Request) {
    // Create a new file
    f := excelize.NewFile()
    defer f.Close()

    // Create buffered writer
    buf := new(bytes.Buffer)
    writer := bufio.NewWriter(buf)

    // Start progress tracking
    progress := 0
    total := 1000000 // Example: 1 million rows

    // Stream data writing
    for i := 0; i < total; i++ {
        // Write row data
        row := []interface{}{
            fmt.Sprintf("Data %d", i),
            time.Now(),
            rand.Float64(),
        }
        cell, _ := excelize.CoordinatesToCellName(1, i+1)
        f.SetSheetRow("Sheet1", cell, &row)

        // Update progress every 1%
        currentProgress := (i * 100) / total
        if currentProgress > progress {
            progress = currentProgress
            // Send progress through WebSocket if needed
            sendProgress(r, progress)
        }
    }

    // Set headers for Excel file
    r.Response.Header().Set("Content-Type", 
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
    r.Response.Header().Set("Content-Disposition", 
        "attachment; filename=large-report.xlsx")

    // Save to response writer
    if err := f.Write(r.Response.Writer); err != nil {
        r.Response.WriteStatus(500)
        return
    }
}

func sendProgress(r *ghttp.Request, progress int) {
    // Implementation depends on your WebSocket setup
    // Example using gorilla/websocket
    if ws, ok := r.GetCtxVar("ws").(*websocket.Conn); ok {
        ws.WriteJSON(map[string]interface{}{
            "progress": progress,
        })
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

3. PDF تولید و بارگیری

func PDFDownloadHandler(r *ghttp.Request) {
    // Create PDF document
    pdf := gofpdf.New("P", "mm", "A4", "")
    pdf.AddPage()

    // Add content
    pdf.SetFont("Arial", "B", 16)
    pdf.Cell(40, 10, "Generated Report")

    // Add table
    pdf.SetFont("Arial", "", 12)
    data := [][]string{
        {"Column 1", "Column 2", "Column 3"},
        {"Data 1", "Data 2", "Data 3"},
        // ... more rows
    }

    for i, row := range data {
        for j, col := range row {
            pdf.Cell(40, 10, col)
            if j == len(row)-1 {
                pdf.Ln(-1)
            }
        }
        if i == 0 {
            pdf.Ln(-1)
        }
    }

    // Set headers
    r.Response.Header().Set("Content-Type", "application/pdf")
    r.Response.Header().Set("Content-Disposition", 
        "attachment; filename=report.pdf")

    // Write to response
    if err := pdf.Output(r.Response.Writer); err != nil {
        r.Response.WriteStatus(500)
        return
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

GOTCHA های مشترک برای جلوگیری از

  1. نشت حافظه

    • همیشه استفاده کنید defer برای پاکسازی
    • کل پرونده ها را در حافظه نمی خوانید
  2. مسائل امنیتی

    • مسیرهای پرونده را تأیید کنید
    • مجوزهای پرونده را بررسی کنید
    • انواع پرونده را محدود کنید
  3. مشکلات عملکرد

    • از خواندن بافر استفاده کنید
    • حافظه پنهان مناسب را اجرا کنید
    • بستن پرونده ها را فراموش نکنید

مثال در دنیای واقعی: یک سرویس بارگیری کامل

در اینجا یک سرویس بارگیری آماده تولید با ترکیب تمام مفاهیمی که مورد بحث قرار داده ایم آورده شده است:

type DownloadService struct {
    limiter  *rate.Limiter
    cache    *Cache
    metrics  *Metrics
}

func (s *DownloadService) ServeDownload(r *ghttp.Request) {
    // 1. Rate limiting
    if err := s.limiter.Wait(r.Context()); err != nil {
        r.Response.WriteStatus(429)
        return
    }

    // 2. Try cache
    if data := s.cache.Get(r.Get("file").String()); data != nil {
        s.metrics.RecordHit()
        return s.serveContent(r, data)
    }

    // 3. Stream file with resume support
    s.streamWithResume(r)
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

آزمایش اجرای خود

فراموش نکنید که آزمایش کنید! در اینجا یک مورد تست سریع برای شروع کار وجود دارد:

func TestDownload(t *testing.T) {
    s := g.Server()
    s.BindHandler("/download", DownloadHandler)

    client := g.Client()
    r, err := client.Get("/download?file=test.txt")

    assert.Nil(t, err)
    assert.Equal(t, 200, r.StatusCode)
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

بسته بندی

ساخت و ساز بارگیری فایل های آماده تولید با Goframe فقط مربوط به تماس نیست ServeFileDownloadبشر این در مورد:

  • رسیدگی به امنیت به درستی
  • مدیریت منابع به طور کارآمد
  • پشتیبانی از قابلیت های رزومه
  • نظارت و حفظ عملکرد

چه چیزی بعدی؟

  • ردیابی پیشرفت را برای بارگیری اضافه کنید
  • ادغام ذخیره سازی ابری را اجرا کنید
  • پشتیبانی از پیش پردازش پرونده را اضافه کنید
  • فشرده سازی جریان را کاوش کنید

منابع

اگر سؤالی دارید یا می خواهید تجربیات خود را با بارگیری فایل در Go به اشتراک بگذارید ، در نظرات به من اطلاع دهید! 💬


مثل این مقاله؟ برای بیشتر نکات و آموزش های توسعه بیشتر مرا دنبال کنید! 🚀

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

همچنین ببینید
بستن
دکمه بازگشت به بالا