// Build returns the zipped contents of the function. func (f *Function) Build() (io.Reader, error) { f.Log.Debugf("creating build") if err := f.hook(BuildHook); err != nil { return nil, err } buf := new(bytes.Buffer) zip := archive.NewZipWriter(buf) files, err := utils.LoadFiles(f.Path, f.IgnoredPatterns) if err != nil { return nil, err } for path, file := range files { f.Log.WithField("file", path).Debug("add file to zip") if err := zip.AddFile(path, file); err != nil { return nil, err } if err := file.Close(); err != nil { return nil, err } } if err := zip.Close(); err != nil { return nil, err } return buf, nil }
func main() { a := archive.NewZipWriter(os.Stdout) f, _ := os.Open("ping.txt") a.AddFile(f.Name(), f) f, _ = os.Open("pong.txt") a.AddFile(f.Name(), f) a.Close() }
// Build returns the zipped contents of the function. func (f *Function) Build() (io.Reader, error) { f.Log.Debugf("creating build") if err := f.RunHook("build"); err != nil { return nil, err } buf := new(bytes.Buffer) zip := archive.NewZipWriter(buf) if r, ok := f.runtime.(runtime.CompiledRuntime); ok { f.Log.Debugf("compiling") if err := r.Build(f.Path); err != nil { return nil, fmt.Errorf("compiling: %s", err) } } if f.env != nil { f.Log.Debugf("adding .env.json") b, err := json.Marshal(f.env) if err != nil { return nil, err } zip.AddBytes(".env.json", b) } if f.runtime.Shimmed() { f.Log.Debugf("adding nodejs shim") zip.AddBytes("index.js", shim.MustAsset("index.js")) zip.AddBytes("byline.js", shim.MustAsset("byline.js")) } files, err := utils.LoadFiles(f.Path, f.IgnoredPatterns) if err != nil { return nil, err } for path, file := range files { f.Log.WithField("file", path).Debug("add file") if err := zip.AddFile(path, file); err != nil { return nil, err } if err := file.Close(); err != nil { return nil, err } } if err := zip.Close(); err != nil { return nil, err } return buf, nil }
func handle(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/zip") w.WriteHeader(200) //write .zip archive directly into response a := archive.NewZipWriter(w) f, _ := os.Open(LARGE_FILE) log.Println("sending...") t0 := time.Now() a.AddFile("file.bin", f) a.Close() log.Printf("sent in %s", time.Now().Sub(t0)) }
// Zip returns the zipped contents of the function. func (f *Function) Zip() (io.Reader, error) { buf := new(bytes.Buffer) zip := archive.NewZipWriter(buf) if r, ok := f.runtime.(runtime.CompiledRuntime); ok { f.Log.Debugf("compiling") if err := r.Build(f.Path); err != nil { return nil, fmt.Errorf("compiling: %s", err) } } if f.env != nil { f.Log.Debugf("adding .env.json") b, err := json.Marshal(f.env) if err != nil { return nil, err } zip.AddBytes(".env.json", b) } if f.runtime.Shimmed() { f.Log.Debugf("adding nodejs shim") zip.AddBytes("index.js", shim.MustAsset("index.js")) zip.AddBytes("byline.js", shim.MustAsset("byline.js")) } for path, file := range f.files { if err := zip.AddFile(path, file); err != nil { return nil, err } defer file.Close() } if err := zip.Close(); err != nil { return nil, err } return buf, nil }
//implement the http.Handler interface func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { path := r.URL.RequestURI() //forward users to README if path == "/" { w.Header().Set("Location", "https://github.com/jpillora/webfont-downloader") w.WriteHeader(http.StatusFound) w.Write([]byte("Redirecting...")) return } //heroku anti-idle endpoint if path == "/ping" { w.Write([]byte("Pong")) return } //only allow sane requests m := pathParser.FindStringSubmatch(path) if len(m) == 0 { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Invalid request: " + path)) return } //set request ID for logging h.count++ requestID := h.count //determine settings fontType := "woff" //default if m[1] != "" { fontType = m[2] } query := m[3] name := nonwords.ReplaceAllString(m[4], "") ua := "" if fontType == "detect" { ua = r.Header.Get("User-Agent") } else { ua = userAgents[fontType] } if ua == "" { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Could not resolve font type")) return } //fetch css file cssBytes, err := h.fetch(ua, baseURL+query) if err != nil { w.WriteHeader(http.StatusBadGateway) w.Write([]byte(err.Error())) return } //stream zip file w.Header().Set("Content-Disposition", "attachment; filename="+name+".zip;") w.WriteHeader(http.StatusOK) zip := archive.NewZipWriter(w) log.Printf("[#%04d] Creating '%s' archive (%s)...", requestID, name, query) fileFetches := sync.WaitGroup{} //1 transform css file and insert in zip //2 async fetch each font import and insert in zip fileID := 1 cssStr := cssURL.ReplaceAllStringFunc(string(cssBytes), func(url string) string { m = cssURL.FindStringSubmatch(url) //parse url ext := m[2] remoteUrl := m[1] + "." + ext localPath := fmt.Sprintf("%s-%d.%s", name, fileID, ext) fileID++ fileFetches.Add(1) //async fetch go func() { defer fileFetches.Done() out, err := h.fetch(ua, remoteUrl) if err != nil { return } log.Printf("[#%04d] Fetched %s, %d bytes", requestID, remoteUrl, len(out)) zip.AddBytes(localPath, out) }() //swap remote url with local url return "url(./" + localPath + ")" }) //insert transformed css file zip.AddBytes(name+".css", []byte(cssStr)) //wait for all fetches to complete fileFetches.Wait() //finalize archive log.Printf("[#%04d] Finalize '%s.zip'", requestID, name) zip.Close() }