goframe框架实现文件下载功能
最近在使用goframe框架编写微服务,接到pm的新需求,要求实现文件下载功能,但是翻看了goframe框架的官方技术文档,没有找到类似的案例。于是,谷歌了一下golang语言的文件下载功能实现的代码,结合goframe框架的源码,实现了goframe框架的文件下载。
探索过程
golang的实现方案
func FileDownload(w http.ResponseWriter, r *http.Request) {
filename := get_filename_from_request(r)
file, _ := os.Open(filename)
defer file.Close()
fileHeader := make([]byte, 512)
file.Read(fileHeader)
fileStat, _ := file.Stat()
w.Header().Set("Content-Disposition", "attachment; filename=" + filename)
w.Header().Set("Content-Type", http.DetectContentType(fileHeader))
w.Header().Set("Content-Length", strconv.FormatInt(fileStat.Size(), 10))
file.Seek(0, 0)
io.Copy(w, file)
return
}
从上面的代码中我们能够发现,核心的代码是http.ResponseWriter
在header中设置了Content-Disposition
,而http的header中的key的详解可以参考http协议规范。我们再来看看goframe框架中的http.ResponseWriter
在哪。
标准的goframe api代码
func (c *Controller) Index(r *ghttp.Request) {
r.Response.Write("hello world")
}
我们追踪一下r.Response
的定义
ghttp_request.go
type Request struct {
*http.Request
Server *Server // Server.
Cookie *Cookie // Cookie.
Session *gsession.Session // Session.
Response *Response // Corresponding Response of this request.
Router *Router // Matched Router for this request. Note that it's not available in HOOK handler.
EnterTime int64 // Request starting time in microseconds.
LeaveTime int64 // Request ending time in microseconds.
Middleware *Middleware // Middleware manager.
StaticFile *StaticFile // Static file object for static file serving.
context context.Context // Custom context for internal usage purpose.
handlers []*handlerParsedItem // All matched handlers containing handler, hook and middleware for this request.
hasHookHandler bool // A bool marking whether there's hook handler in the handlers for performance purpose.
hasServeHandler bool // A bool marking whether there's serving handler in the handlers for performance purpose.
parsedQuery bool // A bool marking whether the GET parameters parsed.
parsedBody bool // A bool marking whether the request body parsed.
parsedForm bool // A bool marking whether request Form parsed for HTTP method PUT, POST, PATCH.
paramsMap map[string]interface{} // Custom parameters map.
routerMap map[string]string // Router parameters map, which might be nil if there're no router parameters.
queryMap map[string]interface{} // Query parameters map, which is nil if there's no query string.
formMap map[string]interface{} // Form parameters map, which is nil if there's no form data from client.
bodyMap map[string]interface{} // Body parameters map, which might be nil if there're no body content.
error error // Current executing error of the request.
exit bool // A bool marking whether current request is exited.
parsedHost string // The parsed host name for current host used by GetHost function.
clientIp string // The parsed client ip for current host used by GetClientIp function.
bodyContent []byte // Request body content.
isFileRequest bool // A bool marking whether current request is file serving.
viewObject *gview.View // Custom template view engine object for this response.
viewParams gview.Params // Custom template view variables for this response.
}
ghttp_response.go
type Response struct {
*ResponseWriter // Underlying ResponseWriter.
Server *Server // Parent server.
Writer *ResponseWriter // Alias of ResponseWriter.
Request *Request // According request.
}
ghttp_response_writer.go
type ResponseWriter struct {
Status int // HTTP status.
writer http.ResponseWriter // The underlying ResponseWriter.
buffer *bytes.Buffer // The output buffer.
hijacked bool // Mark this request is hijacked or not.
wroteHeader bool // Is header wrote or not, avoiding error: superfluous/multiple response.WriteHeader call.
}
至此我们发现ghttp模块实现了强大的http server。可以了解怎么去实现文件下载的功能了。
代码实现
func JsonFile(r *ghttp.Request, data interface{}, filename string) {
// 这里偷懒,content-type写死了
r.Response.Header().Set("Content-Type", "application/octet-stream")
// 注意后面的attachment和filename,否则下载的时候不能自动命名文件名和文件后缀
r.Response.Header().Set("Content-Disposition", "attachment; filename="+filename)
r.Response.Write(data)
r.Exit()
}
关键代码:r.Response.Header().Set("Content-Disposition", "attachment; filename="+filename)