package main import ( "fmt" "io" "log" "mime/multipart" "net/http" "os" "path/filepath" "strconv" "strings" "time" ) func getPort(default_port string) string { port := os.Getenv("PORT") if port == "" { return default_port } return port } func indexHandler(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "text/html") http.ServeFile(w, r, "./index.html") } const UPLOADS_FOLDER_PREFIX = "./uploads/" func saveFilesOnDisk(files []*multipart.FileHeader) (folderPath string, filesPaths []string, error error) { // Create folder for uploaded files folderPath = UPLOADS_FOLDER_PREFIX + strconv.FormatInt(time.Now().Unix(), 10) if err := os.MkdirAll(folderPath, os.ModePerm); err != nil { return "", filesPaths, err } filesPaths = make([]string, len(files)) for i, fileHeader := range files { // Open file from request file, err := fileHeader.Open() if err != nil { return "", filesPaths, err } defer file.Close() // Generate and store file name fileName := strconv.Itoa(i) + strings.ToLower(filepath.Ext(fileHeader.Filename)) filesPaths[i] = folderPath + "/" + fileName // Create file on disk dst, err := os.Create(filesPaths[i]) if err != nil { return "", filesPaths, err } defer dst.Close() _, err = io.Copy(dst, file) if err != nil { return "", filesPaths, err } } return folderPath, filesPaths, nil } type ContentType int const ( Images ContentType = iota Office ) func detectContentType(files []*multipart.FileHeader) (contentType ContentType, error error) { for _, file := range files { fileExt := strings.ToLower(filepath.Ext(file.Filename)) switch fileExt { case ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".odt", ".ods", ".odp": if len(files) == 1 { return Office, nil } else { return contentType, fmt.Errorf("Expected one document, got %d", len(files)) } case ".jpg", ".jpeg", ".png", ".pdf": contentType = Images default: return contentType, fmt.Errorf("%s file extension is unsupported yet", fileExt) } } return contentType, nil } const MAX_UPLOAD_SIZE = (1 << 27) func uploadHandler(w http.ResponseWriter, r *http.Request) { // Restrict to POST http method if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Restrict max size of a bunch of files r.Body = http.MaxBytesReader(w, r.Body, MAX_UPLOAD_SIZE) // Parse body if err := r.ParseMultipartForm(MAX_UPLOAD_SIZE); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } files := r.MultipartForm.File["files"] folderPath, filesPaths, err := saveFilesOnDisk(files) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer os.RemoveAll(folderPath) contType, err := detectContentType(files) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } resFile, err := ConvertToPDF(folderPath, filesPaths, contType) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Disposition", "attachment; filename=\"" + filepath.Base(resFile) + "\"") http.ServeFile(w, r, resFile) } func main() { http.HandleFunc("/", indexHandler) http.HandleFunc("/upload", uploadHandler) port := getPort("80") fmt.Printf("Listening on %s\n", port) if err := http.ListenAndServe(":"+port, nil); err != nil { log.Fatal(err) } }