我正在使用golang构建一个简单的测试API,用于上传和下载图像文件(PNG,JPEG,JPG):
/ pic [POST]用于上传图像并将其保存到文件夹; / pic [GET],用于将图像下载到客户端。
我已经成功构建了/ pic [POST],并且图像已成功上传到服务器的文件。我可以在存储文件夹中打开文件。 (在Windows localhost服务器和ubuntu服务器中)
但是,当我构建/ pic [GET]来下载图片时,我可以将文件下载到客户端(我的计算机),但是自从尝试使用其他图像打开它以来,下载的文件已被破坏了查看器(如Gallery或Photoshop)显示为“我们似乎不支持此文件格式”。因此,似乎下载未成功。
在图库中打开文件:关于为什么会发生这种情况以及如何解决的任何想法?
用于下载图片的golang代码如下(省略错误处理):
func PicDownload(w http.ResponseWriter, r *http.Request){
request := make(map[string]string)
reqBody, _ := ioutil.ReadAll(r.Body)
err = json.Unmarshal(reqBody, &request)
// Error handling
file, err := os.OpenFile("./resources/pic/" + request["filename"], os.O_RDONLY, 0666)
// Error handling
buffer := make([]byte, 512)
_, err = file.Read(buffer)
// Error handling
contentType := http.DetectContentType(buffer)
fileStat, _ := file.Stat()
// Set header
w.Header().Set("Content-Disposition", "attachment; filename=" + request["filename"])
w.Header().Set("Content-Type", contentType)
w.Header().Set("Content-Length", strconv.FormatInt(fileStat.Size(), 10))
// Copying the file content to response body
io.Copy(w, file)
return
}
当您从文件中读取前512个字节以确定内容类型时,基础文件流指针将向前移512个字节。当您以后调用io.Copy
时,将从该位置继续读取。
有两种方法可以纠正此问题。
首先是在调用file.Seek(0, io.SeekStart)
之前先调用io.Copy()
。这会将指针放回到文件的开头。此解决方案需要最少的代码量,但意味着两次从文件读取相同的512字节,这会导致一些开销。
[第二种解决方案是使用buffer := make([]byte, fileStat.Size()
创建一个包含整个文件的缓冲区,并使用该缓冲区进行http.DetectContentType()
调用并写入输出(使用w.Write(buffer)
而不是io.Copy()
来写。)这种方法可能有一次将整个文件加载到内存中的缺点,这对于非常大的文件来说并不理想(io.Copy
使用32KB块而不是加载整个文件)。
注意:正如Peter在评论中提到的,您必须确保用户不能通过发布../../
或其他文件名来遍历您的文件系统。