Bagaimana Anda melayani file html statis menggunakan server web go?

91

Bagaimana Anda melayani index.html (atau beberapa file HTML statis lainnya) menggunakan server web go?

Saya hanya ingin file HTML dasar dan statis (seperti artikel, misalnya) yang dapat saya sajikan dari server web go. HTML harus dapat dimodifikasi di luar program go, seperti yang terjadi saat menggunakan template HTML.

Ini adalah server web saya yang hanya menampung teks hard-code ("Halo dunia!").

package main

import (
  "fmt"
  "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintf(w, "Hello world!")
}

func main() {
  http.HandleFunc("/", handler)
  http.ListenAndServe(":3000", nil)
}
nairware
sumber

Jawaban:

144

Tugas itu sangat mudah dengan paket Golang net / http.

Yang perlu Anda lakukan hanyalah:

package main

import (
        "net/http"
)

func main() {
        http.Handle("/", http.FileServer(http.Dir("./static")))
        http.ListenAndServe(":3000", nil)
}

dengan asumsi bahwa file statis ada di folder yang dinamai staticdi direktori root proyek.

Jika ada di dalam folder static, Anda akan memiliki index.htmlpanggilan file http://localhost:3000/yang akan menghasilkan rendering file indeks tersebut daripada mencantumkan semua file yang tersedia.

Selain itu, memanggil file lain di folder itu (misalnya http://localhost:3000/clients.html) akan menampilkan file itu, yang ditampilkan dengan benar oleh browser (setidaknya Chrome, Firefox, dan Safari :))

UPDATE: melayani file dari url yang berbeda dari "/"

Jika Anda ingin melayani file, katakan dari folder di ./publicbawah url: localhost:3000/staticAnda harus menggunakan fungsi tambahan : func StripPrefix(prefix string, h Handler) Handlerseperti ini:

package main

import (
        "net/http"
)

func main() {
        http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./public"))))
        http.ListenAndServe(":3000", nil)
}

Berkat itu, semua file Anda dari ./publictersedia di bawahlocalhost:3000/static

Tanpa http.StripPrefixfungsi, jika Anda mencoba mengakses file localhost:3000/static/test.html, server akan mencarinya./public/static/test.html

Ini karena server memperlakukan seluruh URI sebagai jalur relatif ke file.

Untungnya, ini mudah diselesaikan dengan fungsi bawaan.

Jarema
sumber
3
mengapa pola pegangan /static/tidak /static?
Bryce
Bagaimana Anda menyajikan file html statis jika file tersebut berada di tempat lain di hard drive, di luar proyek sepenuhnya?
iamtoc
Saya mencobanya dari jalur absolut: / Users / username / path / to / file, dan berhasil dengan sukses sama.
iamtoc
@Bryce, karena Anda ingin mencocokkan subtree lengkap ( lihat juga ). Dengan hanya /staticsebagai pola (dan strip-prefix) hanya http://example.org/staticpermintaan yang akan dilayani oleh penangan FileServer. Itu berarti permintaan ke http://example.org/static/dan http://example.org/static/foo.csslain - lain akan gagal atau ditangani oleh penangan lain.
maxschlepzig
Apakah mungkin untuk menyajikan file statis bersama dengan rute istirahat?
Otak
15

Saya lebih suka menggunakan http.ServeFileuntuk ini http.FileServer. Saya ingin penjelajahan direktori dinonaktifkan, 404 yang tepat jika file hilang dan cara mudah untuk kasus khusus file indeks. Dengan cara ini, Anda bisa meletakkan biner yang dibangun ke dalam folder dan itu akan melayani segala sesuatu yang berhubungan dengan biner itu. Tentu saja, Anda dapat menggunakan strings.Replacepada pjika Anda memiliki file yang tersimpan di direktori lain.


func main() {
    fmt.Println("Now Listening on 80")
    http.HandleFunc("/", serveFiles)
    log.Fatal(http.ListenAndServe(":80", nil))
}

func serveFiles(w http.ResponseWriter, r *http.Request) {
    fmt.Println(r.URL.Path)
    p := "." + r.URL.Path
    if p == "./" {
        p = "./static/index.html"
    }
    http.ServeFile(w, r, p)
}
jerblack
sumber
5

BUKAN server FTP: Itu adalah sesuatu yang berbeda dari yang saya maksudkan, yaitu melayani index.htmlberanda, seperti server web biasa. Seperti, ketika saya membuka mydomain.com di browser saya, saya ingin index.htmlmerender.

Hal ini terutama yang dijelaskan oleh " Menulis Aplikasi Web ", dan seperti apa proyek hugo (generator situs html statis).

Ini tentang membaca file, dan menanggapi dengan ContentType "text / html":

func (server *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    err := server.renderFile(w, r.URL.Path)
    if err != nil {
        w.Header().Set("Content-Type", "text/html; charset=utf-8")
        w.WriteHeader(http.StatusNotFound)
        server.fn404(w, r)
    }
}

dengan renderFile()dasarnya membaca dan mengatur jenis yang tepat:

 file, err = ioutil.ReadFile(server.MediaPath + filename)
 if ext != "" {
    w.Header().Set("Content-Type", mime.TypeByExtension(ext))
 }
VonC
sumber
Mungkin saya tidak mengajukan pertanyaan dengan benar. Bukankah ini menyediakan sarana untuk membangun server file, mirip dengan server FTP? Itu adalah sesuatu yang berbeda dari yang saya maksudkan, yaitu melayani beranda index.html, seperti server web biasa. Seperti, ketika saya membuka mydomain.com di browser saya, saya ingin index.html dirender. Saya tidak ingin melihat direktori file yang disimpan di sistem file server web. Saya dapat mengedit pertanyaan untuk mengubah kata-kata jika pertanyaan saya menyesatkan.
nairware
@nairware ok, saya telah menulis ulang jawabannya
VonC
Jadi tidak ada cara untuk membuat situs web klasik di bawah Go yang menggunakan halaman HTML dan halaman server? Haruskah itu situs web statis atau situs web berbasis template?
Spero
0

Ini mudah di golang karena:

package main

import (
    "log"
    "net/http"
)

func main() {
    log.Fatal(http.ListenAndServe(":8080", http.FileServer(http.Dir("."))))
}

`

Anda cukup melakukan ini dan pastikan untuk menyimpan file HTML Anda sebagai index.html

Nishant Raj
sumber
0

Contoh bagaimana custom melayani file mp3:

r := http.NewServeMux()
r.HandleFunc("/file/*", func(w http.ResponseWriter, r *http.Request) {

    // Prepare file path
    pathFile := strings.ReplaceAll(r.RequestURI, "/file/", "./my_path/")
    f, err := os.Open(pathFile)
    if f == nil || err != nil {
        return
    }

    // Read file into memory
    fileBytes, err := ioutil.ReadAll(f)
    if err != nil {
        log.Println(err)
        _, _ = fmt.Fprintf(w, "Error file bytes")
        return
    }

    // Check mime type
    mime := http.DetectContentType(fileBytes)
    if mime != "audio/mpeg" {
        log.Println("Error file type")
        _, _ = fmt.Fprintf(w, "Error file type")
        return
    }

    // Custom headers
    r.Header.Add("Content-Type", "audio/mpeg")
    r.Header.Add("Cache-Control", "must-revalidate, post-check=0, pre-check=0")
    r.Header.Add("Content-Description", "File Transfer")
    r.Header.Add("Content-Disposition", "attachment; filename=file.mp3")
    r.Header.Add("Content-Transfer-Encoding", "binary")
    r.Header.Add("Expires", "0")
    r.Header.Add("Pragma", "public")
    r.Header.Add("Content-Length", strconv.Itoa(len(fileBytes)))
    http.ServeFile(w, r, pathFile)
})
log.Fatal(http.ListenAndServe(":80", r))
vitamin
sumber