Gin es un marco golang
simple y rápido. Este artículo presenta principalmente la configuración de enrutamiento y el uso de gin
(principalmente el método de publicación).
ir >= 1.18
# development
go run main.go
# run development
# https://github.com/cosmtrek/air Live reload for Go apps
air
# build
go build
# or
make build
# run production
# export GIN_MODE=release
./gin-router-web
# server 8080
http://localhost:8080/
# file chunk upload
http://localhost:8080/upload_chunks
# docker deploy
make serve
# #
go mod tidy
[ POST ] /api/form_post
[ POST ] /api/json_post
[ POST ] /api/urlencoded_post
[ POST ] /api/json_and_form_post
[ POST ] /api/xml_post
[ POST ] /api/file_upload
[ POST ] /api/file_chunk_upload
[ GET ] /api/query
func setStaticFS ( r * gin. Engine ) {
// set html template
r . LoadHTMLGlob ( "views/*" )
// set server static
r . StaticFile ( "favicon.ico" , "./public/favicon.ico" )
r . StaticFS ( "/static" , http . Dir ( "public/static" ))
r . StaticFS ( "/upload" , http . Dir ( "upload" ))
}
func (engine *Engine) LoadHTMLGlob(pattern string)
carga el identificador del archivo HTML del patrón global y asocia el resultado con el renderizador HTML.
func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes
establece la ruta relativa a los recursos estáticos
grupo de enrutamiento API
api := r . Group ( "/api" )
{
api . POST ( "/form_post" , formPost )
api . POST ( "/json_post" , jsonPost )
api . POST ( "/urlencoded_post" , urlencodedPost )
api . POST ( "/json_and_form_post" , jsonAndFormPost )
api . POST ( "/xml_post" , xmlPost )
api . POST ( "/file_upload" , fileUpload )
api . GET ( "/list" , func ( c * gin. Context ) {
name := c . Query ( "name" )
message := c . Query ( "message" )
nick := c . DefaultQuery ( "nick" , "anonymous" )
c . JSON ( http . StatusOK , helper . BuildResponse (gin. H {
"name" : name ,
"message" : message ,
"nick" : nick ,
}))
})
}
Los tipos Content-Type
comúnmente utilizados en Headers
de solicitud incluyen text/plain
, text/html
, application/json
, application/x-www-form-urlencoded
, application/xml
y multipart/form-data
, etc.
text/plain
texto planotext/html
documento HTMLapplication/json
datos en formato jsonapplication/x-www-form-urlencoded
enviado mediante el método HTTP POSTapplication/xml
datos en formato xmlapplication/form-data
se utiliza principalmente para cargar archivosMÍMICA
implementación de enrutamiento de ginebra
// User user struct
type User struct {
Name string `json:"name" form:"name" xml:"name"`
Message string `json:"message" form:"message" xml:"message"`
Nick string `json:"nick" form:"nick" xml:"nick"`
}
// FormPost 表单提交
func FormPost ( c * gin. Context ) {
message := c . PostForm ( "message" )
nick := c . DefaultPostForm ( "nick" , "default nick" )
name := c . DefaultPostForm ( "name" , "default name" )
user := User {
Name : name ,
Nick : nick ,
Message : message ,
}
// This way is better
// 下面这种方式 会自动和定义的结构体进行绑定
// user := &User{}
// c.ShouldBind(user)
c . JSON ( http . StatusOK , helper . BuildResponse ( user ))
}
implementación HTML
< form method =" post " action =" /api/form_post " id =" form " >
< div class =" form-item " >
< label for =" name " > name </ label >
< input type =" text " id =" name " name =" name " />
</ div >
< div class =" form-item " >
< label for =" message " > message </ label >
< input type =" text " id =" message " name =" message " />
</ div >
< div class =" form-item " >
< label for =" name " > nick </ label >
< input type =" text " id =" nick " name =" nick " />
</ div >
< button type =" submit " >提交</ button >
</ form >
application/json
implementación de enrutamiento de ginebra
// JSONPost json
func JSONPost ( c * gin. Context ) {
var user User
if err := c . BindJSON ( & user ); err != nil {
c . AbortWithStatusJSON ( http . StatusOK , helper . BuildErrorResponse ( http . StatusBadRequest , "invalid parameter" ))
return
}
c . JSON ( http . StatusOK , helper . BuildResponse ( user ))
}
implementación js
axios ( {
method : "post" ,
url : "/api/json_post" ,
headers : {
"Content-Type" : "application/json" ,
} ,
data ,
} ) . then ( ( res ) => {
console . log ( res . data ) ;
$ ( ".json-msg" ) . text ( `success ${ new Date ( ) } ` ) ;
} ) ;
application/x-www-form-urlencoded
datos de tipoimplementación de ginebra
// UrlencodedPost application/x-www-form-urlencoded
func UrlencodedPost ( c * gin. Context ) {
limit := c . Query ( "limit" )
name := c . PostForm ( "name" )
message := c . PostForm ( "message" )
nick := c . DefaultPostForm ( "nick" , "1231412" )
user := User {
Name : name ,
Nick : nick ,
Message : message ,
}
// This way is better
// 下面这种方式 会自动和定义的结构体进行绑定
// user := &User{}
// c.ShouldBind(user)
log . Printf ( "request query limit: %s n " , limit )
c . JSON ( http . StatusOK , helper . BuildResponse ( user ))
}
implementación js
axios ( {
method : "post" ,
url : "/api/urlencoded_post?name=shineshao" ,
headers : {
"Content-Type" : "application/x-www-form-urlencoded" ,
} ,
data : $ . param ( data ) ,
} ) . then ( ( res ) => {
console . log ( res . data ) ;
$ ( ".urlencoded-msg" ) . text ( `success ${ new Date ( ) } ` ) ;
} ) ;
application/x-www-form-urlencoded
o application/json
Ginebra
//JSONAndFormPost application/json application/x-www-form-urlencoded
func JSONAndFormPost ( c * gin. Context ) {
var user User
if err := c . ShouldBind ( & user ); err != nil {
c . AbortWithStatusJSON ( http . StatusOK , helper . BuildErrorResponse ( http . StatusBadRequest , "invalid parameter" ))
return
}
c . JSON ( http . StatusOK , helper . BuildResponse ( user ))
}
implementación js
// json
axios ( {
method : "post" ,
url : "/api/json_and_form_post" ,
headers : {
"Content-Type" : "application/json" ,
} ,
data ,
} ) . then ( ( res ) => {
console . log ( res . data ) ;
$ ( ".jsonandform-msg" ) . text ( `success application/json data, ${ new Date ( ) } ` ) ;
} ) ;
// x-www-form-urlencoded
axios ( {
method : "post" ,
url : "/api/json_and_form_post" ,
headers : {
"Content-Type" : "application/x-www-form-urlencoded" ,
} ,
data : $ . param ( data ) ,
} ) . then ( ( res ) => {
console . log ( res . data ) ;
$ ( ".jsonandform-msg" ) . text (
`success application/x-www-form-urlencoded data ${ new Date ( ) } `
) ;
} ) ;
application/xml
( application/xml
).implementación de ginebra
//XMLPost xml
func XMLPost ( c * gin. Context ) {
var user User
// c.ShouldBind(&user)
// c.Bind(&user)
if err := c . BindXML ( & user ); err != nil {
c . AbortWithStatusJSON ( http . StatusOK , helper . BuildErrorResponse ( http . StatusBadRequest , "invalid parameter" ))
return
}
c . JSON ( http . StatusOK , helper . BuildResponse ( user ))
}
implementación js
axios ( {
method : "post" ,
url : "/api/xml_post" ,
headers : {
"Content-Type" : "application/xml" ,
} ,
data : `<xml><name> ${ data . name } </name><message> ${ data . message } </message><nick> ${ data . nick } </nick></xml>` ,
} ) ;
multipart/form-data
( multipart/form-data
)gin implementa la carga de archivos (api/upload.go)
func fileUpload ( c * gin. Context ) {
filesUrl := make ([] string , 0 )
form , err := c . MultipartForm ()
if err != nil {
log . Println ( "postMultipleFile error: %s" )
}
files := form . File [ "file" ]
_ , err = os . Stat ( "upload" )
if err != nil {
os . Mkdir ( "upload" , os . ModePerm )
}
for _ , file := range files {
log . Println ( file . Filename )
// Upload the file to specific dst.
if err = c . SaveUploadedFile ( file , "upload/" + file . Filename ); err != nil {
log . Println ( "SaveUploadedFile error: %s" )
return
}
filesUrl = append ( filesUrl , "upload/" + file . Filename )
}
c . JSON ( http . StatusOK , models . BuildResponse (gin. H {
"urls" : filesURL ,
}))
}
implementación HTML
< div >
< form id =" multipleForm " >
< input
type =" file "
name =" file "
id =" file "
multiple =" multiple "
accept =" image/* "
/>
</ form >
< button class =" file_upload " >开始上传文件</ button >
</ div >
implementación js
// 单个文件上传
// var fd = new FormData()
// var file = document.getElementById('file')
// fd.append('file', file.files[0])
axios ( {
method : "post" ,
url : "/api/file_upload" ,
headers : {
"Content-Type" : "application/form-data" ,
} ,
// data:fd // 单个文件上传
data : new FormData ( $ ( "#multipleForm" ) [ 0 ] ) ,
} ) . then ( ( res ) => {
console . log ( res . data ) ;
const urls = res . data . data . urls || [ ] ;
let imgHtml = "" ;
for ( let i = 0 ; i < urls . length ; i ++ ) {
imgHtml += `<div><img style="width: 200px" src="/ ${ urls [ i ] } " /> <div>/ ${ urls [ i ] } </div></div>` ;
}
$ ( ".file_upload-msg" ) . html (
`<div> ${ new Date ( ) } <div>
${ imgHtml }
</div>
</div>`
) ;
} ) ;
Demostración oficial de carga de archivos
El cliente calculará la cantidad de fragmentos de archivo en función del tamaño del archivo y del tamaño que el usuario desea fragmentar. El cliente solicitará la interfaz uno por uno para cargar todos los fragmentos del archivo al servidor.
El servidor acepta los fragmentos de archivos cargados por el cliente, los almacena en caché o crea archivos y los lee hasta que el último fragmento se carga correctamente.
http://localhost:8080/upload_chunks
El lado del servidor utiliza el marco gin del lenguaje go.
type ChunkFile struct {
Name string `json:"name" form:"name"`
Chunk int `json:"chunk" form:"chunk"`
Chunks int `json:"chunks" form:"chunks"`
}
func PathExists ( path string ) ( bool , error ) {
_ , err := os . Stat ( path )
if err == nil {
return true , nil
}
if os . IsNotExist ( err ) {
return false , nil
}
return false , err
}
// 文件分片上传handler
func fileChunkUpload ( c * gin. Context ) {
var chunkFile ChunkFile
r := c . Request
c . Bind ( & chunkFile )
var Buf = make ([] byte , 0 )
// in your case file would be fileupload
file , _ , _ := r . FormFile ( "file" )
log . Println ( "this is " , chunkFile . File )
Buf , _ = ioutil . ReadAll ( file )
filePath := "upload/" + chunkFile . Name
fd , _ := os . OpenFile ( filePath , os . O_RDWR | os . O_CREATE | os . O_APPEND , 0644 )
fd . Write ( Buf )
fd . Close ()
if chunkFile . Chunk + 1 == chunkFile . Chunks {
c . JSON ( http . StatusOK , gin. H {
"state" : "SUCCESS" ,
"url" : "/" + filePath ,
})
} else {
contentType := strings . Split ( c . GetHeader ( "Content-Type" ), "boundary=" )
c . String ( http . StatusOK , contentType [ 1 ])
}
}
Código completo de la interfaz del servidor.
El cliente utiliza el complemento de carga de archivos plupload. La ventaja es que proporciona carga de varias partes, lo que se puede lograr configurando el atributo chunk_size
al crear un objeto (la capa inferior del complemento calculará la cantidad de fragmentos). según el tamaño del archivo y chunk_size
).
var uploader = new plupload . Uploader ( {
runtimes : "html5,flash,silverlight,html4" ,
browse_button : "pickfiles" , // you can pass an id...
container : document . getElementById ( "container" ) , // ... or DOM Element itself
url : "/api/file_chunk_upload" ,
flash_swf_url : "/static/js/Moxie.swf" ,
silverlight_xap_url : "/static/js/Moxie.xap" ,
chunk_size : "100kb" ,
filters : {
max_file_size : "10mb" ,
mime_types : [
{ title : "Image files" , extensions : "jpg,gif,png,jpeg" } ,
{ title : "Zip files" , extensions : "zip" } ,
] ,
} ,
init : {
PostInit : function ( ) {
document . getElementById ( "filelist" ) . innerHTML = "" ;
document . getElementById ( "uploadfiles" ) . onclick = function ( ) {
uploader . start ( ) ;
return false ;
} ;
} ,
FilesAdded : function ( up , files ) {
plupload . each ( files , function ( file ) {
document . getElementById ( "filelist" ) . innerHTML +=
'<div id="' +
file . id +
'">' +
file . name +
" (" +
plupload . formatSize ( file . size ) +
") <b></b></div>" ;
} ) ;
} ,
UploadProgress : function ( up , file ) {
document . getElementById ( file . id ) . getElementsByTagName ( "b" ) [ 0 ] . innerHTML =
"<span>" + file . percent + "%</span>" ;
} ,
Error : function ( up , err ) {
document
. getElementById ( "console" )
. appendChild (
document . createTextNode ( "nError #" + err . code + ": " + err . message )
) ;
} ,
} ,
} ) ;
uploader . bind ( "ChunkUploaded" , function ( up , file , info ) {
// do some chunk related stuff
console . log ( info ) ;
} ) ;
uploader . init ( ) ;
Código completo de carga del archivo del cliente
manifestación