Функция http.ServeContent

Функция http.ServeContent используется для отправки содержимого в HTTP-ответе. В первый параметр передается http.ResponseWriter для записи ответа, во второй параметр — *http.Request для получения информации о запросе, в третий параметр — имя файла в виде строки, в четвертый параметр — время модификации содержимого, в пятый параметр — объект типа io.ReadSeeker, содержимое которого будет отправлено.

Синтаксис

gohttp.ServeContent(w http.ResponseWriter, r *http.Request, name string, modtime time.Time, content io.ReadSeeker)

Пример с файлом

Давайте отправим содержимое файла в ответ на HTTP-запрос:

gopackage main

import (
  "net/http"
  "os"
  "time"
)

func main() {
  http.HandleFunc("/file", func(w http.ResponseWriter, r *http.Request) {
    file, err := os.Open("example.txt")
    if err != nil {
      http.Error(w, "File not found", http.StatusNotFound)
      return
    }
    defer file.Close()

    stat, err := file.Stat()
    if err != nil {
      http.Error(w, "File error", http.StatusInternalServerError)
      return
    }

    http.ServeContent(w, r, stat.Name(), stat.ModTime(), file)
  })

  http.ListenAndServe(":8080", nil)
}

В этом примере открывается файл example.txt, и его содержимое отправляется клиенту. Функция автоматически обрабатывает заголовки кэширования и поддерживает частичную загрузку.

Пример с данными в памяти

Давайте отправим содержимое из памяти, используя bytes.NewReader:

gopackage main

import (
  "bytes"
  "fmt"
  "net/http"
  "time"
)

func main() {
  http.HandleFunc("/data", func(w http.ResponseWriter, r *http.Request) {
    data := []byte("Hello, World!")
    reader := bytes.NewReader(data)

    modTime := time.Now()
    http.ServeContent(w, r, "data.txt", modTime, reader)
  })

  fmt.Println("Server started on :8080")
  http.ListenAndServe(":8080", nil)
}

В этом примере данные хранятся в памяти, но функция http.ServeContent обрабатывает их так же, как если бы они были из файла. Это полезно для динамически генерируемого контента.

Пример с условными запросами

Функция автоматически обрабатывает заголовки If-Modified-Since и If-None-Match. Давайте проверим это на примере:

gopackage main

import (
  "bytes"
  "fmt"
  "net/http"
  "time"
)

func main() {
  http.HandleFunc("/static", func(w http.ResponseWriter, r *http.Request) {
    content := []byte("Static content")
    reader := bytes.NewReader(content)

    modTime := time.Now().Add(-24 * time.Hour)
    http.ServeContent(w, r, "static.txt", modTime, reader)
  })

  fmt.Println("Server started on :8080")
  http.ListenAndServe(":8080", nil)
}

При повторном запросе браузер отправит заголовок If-Modified-Since, и если содержимое не изменилось, сервер ответит статусом 304 Not Modified, что экономит трафик.

Пример с частичной загрузкой (Range запросы)

Функция поддерживает заголовок Range, позволяя клиенту загружать файл по частям:

gopackage main

import (
  "bytes"
  "fmt"
  "net/http"
  "time"
)

func main() {
  http.HandleFunc("/video", func(w http.ResponseWriter, r *http.Request) {
    videoData := bytes.Repeat([]byte("video data "), 100)
    reader := bytes.NewReader(videoData)

    modTime := time.Now()
    http.ServeContent(w, r, "video.mp4", modTime, reader)
  })

  fmt.Println("Server started on :8080")
  http.ListenAndServe(":8080", nil)
}

Если клиент отправит заголовок Range: bytes=0-99, сервер вернет только первые 100 байт. Это особенно полезно для стриминга видео и возобновления загрузок.

Смотрите также

  • функцию http.ServeFile,
    которая упрощает отправку файлов без ручного открытия
  • функцию http.FileServer,
    которая создает обработчик для целого каталога файлов
  • структуру http.ServeMux,
    которая используется для маршрутизации HTTP-запросов
  • функцию http.NotFound,
    которая возвращает клиенту ошибку 404 Not Found