Gin هو إطار عمل golang
بسيط وسريع، تقدم هذه المقالة بشكل أساسي تكوين التوجيه واستخدام gin
(طريقة النشر بشكل أساسي).
جولانج >= 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)
بتحميل معرف ملف HTML الخاص بالنمط العام وربط النتيجة بعارض HTML.
func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes
بتعيين الموارد الثابتة للمسار النسبي
مجموعة توجيه 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 ,
}))
})
}
تتضمن أنواع Content-Type
الشائعة الاستخدام في Headers
الطلبات text/plain
و text/html
و application/json
و application/x-www-form-urlencoded
و application/xml
و multipart/form-data
وما إلى ذلك.
text/plain
نص عاديtext/html
وثيقة HTMLapplication/json
jsonapplication/x-www-form-urlencoded
تم إرسال النموذج باستخدام طريقة HTTP POSTapplication/xml
XMLapplication/form-data
بشكل أساسي لتحميل الملفاتMIME
تنفيذ توجيه جين
// 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 ))
}
تنفيذ أتش تي أم أل
< 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
تنفيذ توجيه جين
// 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 ))
}
تنفيذ شبيبة
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
تنفيذ الجن
// 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 ))
}
تنفيذ شبيبة
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
أو application/json
شرك
//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 ))
}
تنفيذ شبيبة
// 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
)تنفيذ الجن
//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 ))
}
تنفيذ شبيبة
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 تحميل الملف (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 ,
}))
}
تنفيذ أتش تي أم أل
< div >
< form id =" multipleForm " >
< input
type =" file "
name =" file "
id =" file "
multiple =" multiple "
accept =" image/* "
/>
</ form >
< button class =" file_upload " >开始上传文件</ button >
</ div >
تنفيذ شبيبة
// 单个文件上传
// 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>`
) ;
} ) ;
العرض التوضيحي لتحميل الملف الرسمي
سيقوم العميل بحساب عدد أجزاء الملف بناءً على حجم الملف والحجم الذي يريد المستخدم تجزئته. سيطلب العميل من الواجهة واحدًا تلو الآخر لتحميل جميع أجزاء الملف إلى الخادم.
يقبل الخادم أجزاء الملف التي تم تحميلها من قبل العميل، ويخزنها مؤقتًا أو ينشئ ملفات ويقرأ الأجزاء حتى يتم تحميل الجزء الأخير بنجاح.
http://localhost:8080/upload_chunks
يستخدم جانب الخادم إطار عمل gin للغة 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 ])
}
}
رمز كامل لواجهة الخادم
يستخدم العميل البرنامج الإضافي لتحميل الملفات plupload وتتمثل الميزة في أنه يوفر تحميلًا متعدد الأجزاء، والذي يمكن تحقيقه عن طريق تكوين سمة chunk_size
عند إنشاء كائن (ستقوم الطبقة السفلية من المكون الإضافي بحساب عدد الأجزاء. بناءً على حجم الملف و 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 ( ) ;
تحميل ملف العميل رمز كامل
تجريبي