Closed dcsunny closed 3 years ago
可以参考下 examples/http 的例子:https://github.com/go-kratos/kratos/tree/main/examples/http
这个是router单独出来,那通过protobuf生成出来的接口呢?
// defaultRequestDecoder decodes the request body to object.
func defaultRequestDecoder(req *http.Request, v interface{}) error {
subtype := httputil.ContentSubtype(req.Header.Get("Content-Type"))
if codec := encoding.GetCodec(subtype); codec != nil {
data, err := ioutil.ReadAll(req.Body)
if err != nil {
return errors.BadRequest("CODEC", err.Error())
}
if err := codec.Unmarshal(data, v); err != nil {
return errors.BadRequest("CODEC", err.Error())
}
} else {
if err := binding.BindForm(req, v); err != nil {
return errors.BadRequest("CODEC", err.Error())
}
}
return nil
}
就是这块好像不能将上传的文件转成需要的对象
proto 目前不支持文档上传,可以考虑直接手写接口实现
@dcsunny 你好,我现在也有这个困扰,请问你怎么解决的啊?能分享一下吗 谢谢!
可以尝试chunk上传,需要前后端配合。
可以直接写接口, 不过具体代码组织需要自己解决
package main
import (
"fmt"
"io"
"net/http"
"os"
"github.com/go-kratos/kratos/v2/middleware/std"
"github.com/go-kratos/kratos/v2/transport/http"
)
func main() {
httpSrv: = http.NewServer(
http.Address(":8000"),
http.Middleware(std.ServerRecovery()),
)
httpSrv.Handle(http.MethodPost, "/upload", func(w http.ResponseWriter, r * http.Request) {
// 设置文件大小限制
r.ParseMultipartForm(32 << 20)
file, header, err: = r.FormFile("file")
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
defer file.Close()
// 检查文件大小
if header.Size > (10 << 20) { // 限制文件大小为10MB
http.Error(w, "file size exceeds the limit", http.StatusBadRequest)
return
}
// 检查文件类型
ext: = path.Ext(header.Filename)
if ext != ".jpg" && ext != ".png" { // 限制文件类型为jpg和png
http.Error(w, "unsupported file type", http.StatusBadRequest)
return
}
// 创建一个新的文件
dst, err: = os.Create(header.Filename)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer dst.Close()
// 将上传的文件内容复制到目标文件中
if _, err: = io.Copy(dst, file);
err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// 返回成功响应
fmt.Fprintln(w, "File uploaded successfully")
})
if err: = httpSrv.Start();err != nil {
panic(err)
}
}
func TestUploadFile(t *testing.T) {
file, err := os.Open("testdata/test.jpg") // 测试文件路径
if err != nil {
t.Fatal(err)
}
defer file.Close()
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("file", filepath.Base(file.Name()))
if err != nil {
t.Fatal(err)
}
if _, err = io.Copy(part, file); err != nil {
t.Fatal(err)
}
if err = writer.Close(); err != nil {
t.Fatal(err)
}
req, err := http.NewRequest(http.MethodPost, "/upload", body)
if err != nil {
t.Fatal(err)
}
req.Header.Set("Content-Type", writer.FormDataContentType())
rr := httptest.NewRecorder()
handler := http.HandlerFunc(uploadHandler)
handler.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
}
// 检查文件是否成功上传到服务器
_, err = os.Stat("test.jpg")
if err != nil {
t.Errorf("file not found on server")
}
}
// UploadRequestDecoder CustomRequestDecoder 将用户上传的文件序列化后写入Request Body中
func UploadRequestDecoder() http.ServerOption {
return http.RequestDecoder(func(r *http.Request, v interface{}) error {
// 拦截所有上传请求
if r.URL.Path != "/upload" {
return nil
}
codec, ok := http.CodecForRequest(r, "Content-Type")
if !ok {
return errors.BadRequest("CODEC", fmt.Sprintf("unregister Content-Type: %s", r.Header.Get("Content-Type")))
}
file, header, err := r.FormFile("file")
if err != nil {
return errors.BadRequest("CODEC", err.Error())
}
if ext := path.Ext(header.Filename); ext != ".xlsx" { // 限制文件类型为xlsx
return errors.BadRequest("CODEC", err.Error())
}
reqData, err := handleExcelAttachment(r, file)
if err != nil {
return err
}
// 序列化之后写入body
var buf []byte
defer func() {
// 给 BODY 重新赋值
r.Body = io.NopCloser(bytes.NewBuffer(buf))
}()
if err = codec.Unmarshal(buf, reqData); err != nil {
return errors.BadRequest("CODEC", fmt.Sprintf("body unmarshal %s", err.Error()))
}
return nil
})
}
试了一下这样写一个opt也获取不到文件数据,有大佬看看为啥吗
有些业务需要用到文件上传接口,但是发现这个框架好像不支持? 如果支持应该如何使用