在Go语言中计算MultipartForm文件上传的SHA1哈希值。

huangapple go评论159阅读模式
英文:

Calculating SHA1 hash on MultipartForm file upload in Go

问题

我正在尝试计算上传文件的SHA1哈希值,但目前遇到了困难。示例代码如下:

err := req.ParseMultipartForm(200000)
if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
}

m := req.MultipartForm
files := m.File["Filedata"]

for i, _ := range files {
    file, err := files[i].Open()
    defer file.Close()
    fh := getFileHash(file)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        break
    }

    dst, err := os.Create(baseDir + fh + ".jpg")
    defer dst.Close()
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        break
    }

    if _, err := io.Copy(dst, file); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        break
    }
}

getFileHash函数的内容如下:

func getFileHash(f *os.File) string {
    fstat, err := f.Stat()
    if err != nil {
        panic(err)
    }
    fSize := fstat.Size()
    buf := make([]byte, fSize)
    hash := sha1.New()
    n, err := f.Read(buf)
    if _, err := io.WriteString(hash, string(buf[:n])); err != nil {
        panic(err)
    }
    return string(hash.Sum(nil))
}

我遇到了以下错误,但不确定如何解决:

*cannot use file (type multipart.File) as type os.File in function argument: need type assertion

如果我使用一个示例哈希值作为"fh"的值进行测试,它可以正常工作。

我知道我可以先保存文件,计算哈希值,然后再移动文件,但如果可能的话,我不想多做这些步骤。如果你能提供任何帮助,我将不胜感激。

谢谢!

英文:

I'm trying to calculate the sha1 hash of an uploaded file but so far I'm at a dead end. The sample code is as follows:

err := req.ParseMultipartForm(200000)
if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
	return
}

m := req.MultipartForm
files := m.File["Filedata"]

for i, _ := range files {
        file, err := files[i].Open()
        defer file.Close()
	fh = getFileHash(file)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		break
	}

	dst, err := os.Create(baseDir+fh+".jpg")
	defer dst.Close()
	if err != nil {
	        http.Error(w, err.Error(), http.StatusInternalServerError)
	        break
	}

	if _, err := io.Copy(dst, file); err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                break
	}
}

And the content of the getFileHash function is as follows:

func getFileHash (f *os.File) string {
     fstat, err := f.Stat()
     if err != nil {
	panic(err)
     }
     fSize := fstat.Size()
     buf := make([]byte, fSize)
     hash := sha1.New()
     n, err := f.Read(buf)
     if _, err := io.WriteString(hash, string(buf[:n])); err != nil {
           panic(err)
     }
     return string(hash.Sum(nil))
}

I'm getting the following error although I'm not sure how to get around this:

*cannot use file (type multipart.File) as type os.File in function argument: need type assertion

If I test by using a sample hash as the value of "fh", it works just fine.

I know I could simply save the file, calculate the hash, and then move the file although that's extra steps I'd rather not take if possible. I'd appreciate any help you could provide me with.

Thanks!

答案1

得分: 1

你可以同时将文件流式传输到磁盘和哈希函数中。这里有一个简单的示例:

注意:它有一个限制:不会检查写入的文件是否出错。这可能会根据你的文件系统产生重大影响。在生产代码中,你可以考虑使用类似 errutil 的东西。

play链接

package main

import (
	"crypto/sha1"
	"fmt"
	"io"
	"os"
	"strings"
)

func shaAndSave(src io.Reader) ([]byte, error) {
	h := sha1.New()
	dest, err := os.Create("tmpfile.txt")
	if err != nil {
		return nil, err
	}
	defer dest.Close()
	t := io.TeeReader(src, h)
	_, err = io.Copy(dest, t)
	if err != nil {
		return nil, err
	}

	return h.Sum(nil), nil
}

func main() {
	src := strings.NewReader("pretend this was a file")

	hashBytes, err := shaAndSave(src)
	fmt.Printf("Returned %x and %v", hashBytes, err)
}

希望对你有帮助!

英文:

You can stream the file to disk and a hash function at the same time. Here's a rough example:

Note: It does have one caveat: The error is not checked on the file being written. This can matter tremendously depending on your filesystem. You might consider something like errutil in production code.

play link

package main

import (
	"crypto/sha1"
	"fmt"
	"io"
	"os"
	"strings"
)

func shaAndSave(src io.Reader) ([]byte, error) {
	h := sha1.New()
	dest, err := os.Create("tmpfile.txt")
	if err != nil {
		return nil, err
	}
	defer dest.Close()
	t := io.TeeReader(src, h)
	_, err = io.Copy(dest, t)
	if err != nil {
		return nil, err
	}

	return h.Sum(nil), nil
}

func main() {
	src := strings.NewReader("pretend this was a file")

	hashBytes, err := shaAndSave(src)
	fmt.Printf("Returned %x and %v", hashBytes, err)
}

答案2

得分: 0

从http://golang.org/pkg/mime/multipart/#File

File是访问多部分消息中文件部分的接口。它的内容可以存储在内存中或磁盘上。如果存储在磁盘上,File的底层具体类型将是*os.File。

所以,你确实没有传递*os.File类型,因为它是在内存中。尝试接受multipart.File作为类型。

英文:

From http://golang.org/pkg/mime/multipart/#File

> File is an interface to access the file part of a multipart message. Its contents may be either stored in memory or on disk. If stored on disk, the File's underlying concrete type will be an *os.File.

So you are indeed not passing an *os.File type, since it is in memory. Try accepting multipart.File as a type.

huangapple
  • 本文由 发表于 2014年7月17日 02:29:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/24788093.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定