Golang HTTPファイルアップロード

3960 ワード

まず、サーバー側で二つのルートを設定して、ファイルのアップロードに使います。/files/*はファイルのダウンロードに使います。
const maxUploadSize = 2 * 1024 * 2014 // 2 MB 
const uploadPath = "./tmp"

func main() {
    http.HandleFunc("/upload", uploadFileHandler())

    fs := http.FileServer(http.Dir(uploadPath))
    http.Handle("/files/", http.StripPrefix("/files", fs))

    log.Print("Server started on localhost:8080, use /upload for uploading files and /files/{fileName} for downloading files.")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
   アップロードするターゲットディレクトリと、私たちが受ける最大ファイルサイズを定数として定義します。ここで注意してください。ファイルサービス全体の概念はこのように簡単です。私たちは標準ライブラリのツールだけを使って、http.FileServerを使ってHTTP処理プログラムを作成します。http.Dir(uplloadPath)が提供するディレクトリを使ってファイルをアップロードします。
  今はuploadFileHanderを実現するだけです。この処理手順には以下の機能が含まれます。
       ·ファイルの最大値を検証します。
       ·要求からファイルとPOSTパラメータを検証する
       ·提供されたファイルの種類を確認します。
       ·ランダムファイル名を作成
       ·ファイルをハードディスクに書き込みます。
       ·すべてのエラーを処理して、うまくいけば成功メッセージに戻ります。
   第一歩は、処理手順を定義します。
func uploadFileHandler() http.HandlerFunc {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   その後、http.MaxBytes Readerを使ってファイルサイズを検証します。ファイルサイズが設定値より大きい場合、エラーを返します。エラーは、エラー情報と対応するHTTP状態コードを返すヘルパープログラムrender Errerによって処理されます。
r.Body = http.MaxBytesReader(w, r.Body, maxUploadSize)
    if err := r.ParseMultipartForm(maxUploadSize); err != nil {
        renderError(w, "FILE_TOO_BIG", http.StatusBadRequest)
        return
    }
   ファイルサイズの検証が通れば、フォームパラメータの種類とアップロードされたファイルを確認して解析し、ファイルを読みます。この例では、Wieldを明確にするために、派手なio.Readerとio.Writerインターフェースを使用しません。簡単にファイルをバイト配列に読み込むだけです。これは後で書きます。
fileType := r.PostFormValue("type")
    file, _, err := r.FormFile("uploadFile")
    if err != nil {
        renderError(w, "INVALID_FILE", http.StatusBadRequest)
        return
    }
    defer file.Close()
    fileBytes, err := ioutil.ReadAll(file)
    if err != nil {
        renderError(w, "INVALID_FILE", http.StatusBadRequest)
        return
    }
   ファイルのサイズを確認し、ファイルを読み込みました。次はファイルの種類を確認します。安いですが、安全ではない方法で、ファイルの拡張子だけを確認して、ユーザーがそれを変えていないと信じていますが、正式なプロジェクトに対してはそうすべきではありません。
  幸いにも、Go標準ライブラリは私たちにHttp.DetectContectType関数を提供しています。この関数はmimesniffアルゴリズムに基づいています。ファイルを読み込む512バイトが必要であれば、ファイルの種類を判断することができます。
iletype := http.DetectContentType(fileBytes)
    if filetype != "image/jpeg" && filetype != "image/jpg" &&
        filetype != "image/gif" && filetype != "image/png" &&
        filetype != "application/pdf" {
        renderError(w, "INVALID_FILE_TYPE", http.StatusBadRequest)
        return
    }
   実際のアプリケーションでは、ファイルメタデータを使用して、データベースに保存したり、外部サービスにプッシュしたりすることがあります。メタデータを解析して操作します。ここではランダムな新しい名前を作成し、新しいファイル名を記録します。
fileName := randToken(12)
    fileEndings, err := mime.ExtensionsByType(fileType)
    if err != nil {
        renderError(w, "CANT_READ_FILE_TYPE", http.StatusInternalServerError)
        return
    }
    newPath := filepath.Join(uploadPath, fileName+fileEndings[0])
    fmt.Printf("FileType: %s, File: %s
", fileType, newPath)
   すぐに完成しました。重要なステップだけが残っています。書類を書きます。上記で提供したように、私たちは読み込んだバイナリファイルを新たに作成したnewFileというファイル処理プログラムにコピーするだけです。
   すべての部分が大丈夫なら、私達はユーザーにi額SUCCESS情報を返します。
newFile, err := os.Create(newPath)
    if err != nil {
        renderError(w, "CANT_WRITE_FILE", http.StatusInternalServerError)
        return
    }
    defer newFile.Close()
    if _, err := newFile.Write(fileBytes); err != nil {
        renderError(w, "CANT_WRITE_FILE", http.StatusInternalServerError)
        return
    }
    w.Write([]byte("SUCCESS"))
   これでいいです。この簡単な例をテストして、仮想ファイルを使ってHTMLページをアップロードしてもいいです。
 
参考:
https://www.yuque.com/docs/share/b5d6f032-9f65-48b4-b0fb-87a05bc72672