The last article introduced the configuration of database connection and routing. This article started to write the more important functions, receiving and parsing md files, then storing important information into the database, and finally, through an interface, converting MD into HTML strings and exposing them.
Handles file upload interface
After receiving a file, we start to read the file, save the important information in the file to the database, in order to be able to traverse the specified blog information through the key information, and then return to the front end for display.
First we create a controller that handles the uploaded file in the controllers directory:
controllers/uploadFile.go :
The individual templates created using XORM are introduced and then used to store the data into the database
package controllers import ( "crypto/md5" "errors" "fmt" ... "Github.com/gin-gonic/gin" github.com/zachrey/blog/models) const postDir = ". / posts / / / the file "method in a specified directory, If too many files can be placed on the present various clouds. Func UpLoadFile(c *gin.Context) {UpLoadFile(c *gin.Context) { File, header, err := c.equest. FormFile("upload") // Get the file name to upload, Filename := header. filename Sprintf("%x", Md fileExt := filepath.Ext(postDir + filename) // File storage path filepath := postDir + Log.Println("[INFO] upload file: HasSameNameFile (md5FileName+fileExt, hasSameNameFile(md5FileName+fileExt, hasSameNameFile) postDir) if has { c.JSON(http.StatusOK, gin.H{ "status": 1, "msg": "The server already has the same file name ",}) return} // Create a file on the server according to the MD5 value of the file name out, err := os.Create(filePath) if err! = nil {log.fatal (err)} defer out.close () // Add information to database... // Add information to database... // Add information to database... c.JSON(http.StatusOK, gin.H{ "status": 0, "msg": Func hasSameNameFile(fileName, dir string) bool {files, _ := ioutil.ReadDir(dir) // Traverses the directory, whether the same file exists, There are two ways to check the name of the file, you can go to the database to see if there is the same file name. for _, f := range files { if fileName == f.Name() { return true } } return false }Copy the code
Writes file parsing information to the database
The main information of a blog includes: title, classification, label and word count, etc. Here we stipulate how to write these information in the MD file, so as to facilitate the server to parse and then store in the database. Specify that the first few lines of the MD file should contain information as follows:
Title: title 1 categories: category 1 and category 2, the classified three label: JS, ES6 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --Copy the code
Information content and the main content to — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — for segmentation. Then read the file, write the data to the database, write the logic to the database again in the same file, let’s complete the content, above the blank “database operation” is the following content.
controllers/uploadFile.go :
package controllers import ( "crypto/md5" "errors" "fmt" ... "github.com/gin-gonic/gin" "github.com/zachrey/blog/models" ) ... Var gr sync.waitgroup var isShouldRemove = false // UpLoadFile UpLoadFile(c *gin.Context) {... // Create a file on the server according to the MD5 value of the file name out, err := os.Create(filePath) if err! = nil {log.fatal (err)} // Whether to delete the created file after the upload process is complete. If there are any errors, delete the newly created file if parsing errors occur. This operation must be placed on out.close because the file stream created cannot be deleted without closing it, and because of the order in which the stack defer is executed, it must be declared on top. defer func() { if isShouldRemove { err = os.Remove(filePath) if err ! = nil {log.println ("[ERROR] ", err)}}}() // Close file stream, store file. Defer out.close () // copy the contents of the uploaded file into the new file and save it. _, err = io.Copy(out, file) if err ! Fatal(err)} // If there is an error reading the parse file, isShouldRemove copies to true and finally defer to delete err = readMdFileInfo(filePath) if err! = nil { isShouldRemove = true c.JSON(http.StatusOK, gin.H{ "status": 1, "msg": err.Error(), }) return } ... // Return json data, please refer to the previous paragraph. Func readMdFileInfo(filePath String) error { _ := ioutil.ReadFile(filePath) // Cut the content into each line := strings.split (string(fileread), "\n") // Body := strings.Join(lines[5:], TextAmount := GetStrLength(body) log.println (lines) const (TITLE = "TITLE: " CATEGORIES = "categories: " LABEL = "label: " ) var ( postId int64 postCh chan int64 categoryCh chan []int64 labelCh chan []int64 ) mdInfo := Make (map[string]string) /** * insert three groups of information in parallel */ for I, lens := 0, len(lines); i < lens && i < 5; HasPrefix(lines[I], TITLE): MdInfo [TITLE] = strings.TrimLeft(lines[I], TITLE) postCh = make(chan int64) Go models.insertPost (mdInfo[TITLE], filepath.Base(filepath), int64(textAmount), postCh) case strings.HasPrefix(lines[i], CATEGORIES): mdInfo[CATEGORIES] = strings.TrimLeft(lines[i], CATEGORIES) categoryCh = make(chan []int64) go models.insertcategory (mdInfo[] CATEGORIES], categoryCh) case strings.HasPrefix(lines[i], LABEL): mdInfo[LABEL] = strings.TrimLeft(lines[i], LabelCh = make(chan []int64) go models.InsertLabel(mdInfo[LABEL], LabelCh)}} /** * insert related information */ postId = < -postch // wait for the article to be successfully inserted if postId == 0 {return errors.New(" the same article title on the server ")} Log. Println("[INFO] postId: ", postId) = nil { go func() { categoryIds := <-categoryCh log.Println("[INFO] categoryIds: ", categoryIds) for _, v: = range categoryIds {models. InsertPostAndCategory (postId, v)}} ()} / / insert articles and information associated with a tag, Text and tag association table if labelCh! = nil { go func() { labels := <-labelCh log.Println("[INFO] labels: ", labels) for _, v: = range labels {models. InsertPostAndLabel (postId, v)}}} () return nil / / all successful insertion, it returns no error, if there is a mistake, Return}... // GetStrLength returns the number of words in the input string. Chinese characters and punctuation count as one word, English and other characters count as one word. Less than 1 is 1 func GetStrLength (STR string) float64 {var total float64 reg: = regexp. MustCompile (" / · | |. | | "|" "|" "| |" |; : | | | | 】? | | | (), / ") for _, r: = range STR {if unicode. Is (unicode) Scripts/" Han ", R) | | reg. Match (byte [] (string (r))) {total = total + 1} else {total = total + 0.5}} return math.h Ceil (total)}Copy the code
Here, goroutine and channel methods are used to try to get more familiar with Go features. Three groups of unrelated records are inserted into the database concurrently, and then the three groups of unrelated records are inserted sequentially after each group is inserted.
The routing configuration
routers/router.go:
package routers
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
ctrs "github.com/zachrey/blog/controllers"
"github.com/zachrey/blog/models"
)
...
func loadRouters(router *gin.Engine) {
...
router.POST("/upload", ctrs.UpLoadFile)
...
}
Copy the code
At this point we use Postman to test, start the service, and select file upload.
The last
How to deal with the uploaded MD file is the focus of the whole blog system, save the article data, you can make a variety of API based on these data. In the next article, we will discuss how to implement the CMD tool to upload files. If there are some incomplete code, you can go to my Github, view the complete source code, directly run. Welcome to leave a message or use email ([email protected]) to contact me to discuss, learning practice to share, there are many deficiencies, please give more advice.