6月27实训报告

6月27实训报告

  • 概述
  • 回调 api
      • 文件

概述

  1. 测试的时候碰到问题,添加回调函数解决
  2. 更新 展示文件的接口

回调 api

spider handler

测试的时候,发现当指定所有节点运行时,结果集不一样。经检查发现,同步爬虫文件到其他节点的时候会出现问题:假设有节点 a、b、c,a发布了一个爬虫,b、c 经过了一段时间同步了这个爬虫,而 b 在远端 gridfs 上修改了爬虫文件后 系统出错 没有同步到其他节点,而其他节点不知道 b 的文件是脏数据。此时引入 md5 文件,比对远端 gridfs 字段,进行校验。
package spider_handler

import (
	"constants"
	"database"
	"model"
	"utils"
	"fmt"
	"github.com/apex/log"
	"github.com/globalsign/mgo/bson"
	"github.com/satori/go.uuid"
	"github.com/spf13/viper"
	"io"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"runtime/debug"
	"strings"
	"sync"
)

const (
	Md5File = "md5.txt"
)

type SpiderSync struct {
	Spider model.Spider
}

func (s *SpiderSync) CreateMd5File(md5 string) {
	path := filepath.Join(viper.GetString("spider.path"), s.Spider.Name)
	utils.CreateDirPath(path)

	fileName := filepath.Join(path, Md5File)
	file := utils.OpenFile(fileName)
	defer utils.Close(file)
	if file != nil {
		if _, err := file.WriteString(md5 + "\n"); err != nil {
			log.Errorf("file write string error: %s", err.Error())
			debug.PrintStack()
		}
	}
}

func (s *SpiderSync) CheckIsScrapy() {//拉取下来爬虫文件时,判断解压的文件中有无 scrapy.cfg 字段,用来判断执行什么样的命令.
	if s.Spider.Type == constants.Configurable {
		return
	}
	
	s.Spider.IsScrapy = utils.Exists(path.Join(s.Spider.Src, "scrapy.cfg"))
	if s.Spider.IsScrapy {
		s.Spider.Cmd = "scrapy crawl"
	}
	if err := s.Spider.Save(); err != nil {
		log.Errorf(err.Error())
		debug.PrintStack()
		return
	}
}

func (s *SpiderSync) AfterRemoveDownCreate() {
	if model.IsMaster() {
		s.CheckIsScrapy()
	}
}

func (s *SpiderSync) RemoveDownCreate(md5 string) {
	s.RemoveSpiderFile()
	s.Download()
	s.CreateMd5File(md5)
	s.AfterRemoveDownCreate()
}

// 删除本地文件
func (s *SpiderSync) RemoveSpiderFile() {
	path := filepath.Join(
		viper.GetString("spider.path"),
		s.Spider.Name,
	)
	//爬虫文件有变化,先删除本地文件
	if err := os.RemoveAll(path); err != nil {
		log.Errorf("remove spider files error: %s, path: %s", err.Error(), path)
		debug.PrintStack()
	}
}


// 下载爬虫
func (s *SpiderSync) Download() {
	spiderId := s.Spider.Id.Hex()
	fileId := s.Spider.FileId.Hex()
	isDownloading, key := s.CheckDownLoading(spiderId, fileId)
	if isDownloading {
		log.Infof(fmt.Sprintf("spider is already being downloaded, spider id: %s", s.Spider.Id.Hex()))
		return
	} else {
		_ = database.RedisClient.HSet("spider", key, key)
	}

	session, gf := database.GetGridFs("files")
	defer session.Close()

	f, err := gf.OpenId(bson.ObjectIdHex(fileId))
	defer utils.Close(f)
	if err != nil {
		log.Errorf("open file id: " + fileId + ", spider id:" + spiderId + ", error: " + err.Error())
		debug.PrintStack()
		return
	}

	// 生成唯一ID
	randomId := uuid.NewV4()
	tmpPath := viper.GetString("other.tmppath")
	if !utils.Exists(tmpPath) {
		if err := os.MkdirAll(tmpPath, 0777); err != nil {
			log.Errorf("mkdir other.tmppath error: %v", err.Error())
			return
		}
	}
	// 创建临时文件
	tmpFilePath := filepath.Join(tmpPath, randomId.String()+".zip")
	tmpFile := utils.OpenFile(tmpFilePath)

	// 将该文件写入临时文件
	if _, err := io.Copy(tmpFile, f); err != nil {
		log.Errorf("copy file error: %s, file_id: %s", err.Error(), f.Id())
		debug.PrintStack()
		return
	}

	// 解压缩临时文件到目标文件夹
	dstPath := filepath.Join(
		viper.GetString("spider.path"),
		s.Spider.Name,
	)
	if err := utils.DeCompress(tmpFile, dstPath); err != nil {
		log.Errorf(err.Error())
		debug.PrintStack()
		return
	}

	cmd := exec.Command("chmod", "-R", "777", dstPath)
	if err := cmd.Run(); err != nil {
		log.Errorf(err.Error())
		debug.PrintStack()
		return
	}

	// 关闭临时文件
	if err := tmpFile.Close(); err != nil {
		log.Errorf(err.Error())
		debug.PrintStack()
		return
	}

	// 删除临时文件
	if err := os.Remove(tmpFilePath); err != nil {
		log.Errorf(err.Error())
		debug.PrintStack()
		return
	}

	_ = database.RedisClient.HDel("spider", key)
}


文件

file.go

遍历获取本地爬虫文件夹的结构,返回 file 结构体,其中的 children 层层嵌套,用于前端的文件目录展示。
package services

import (
	"model"
	"github.com/apex/log"
	"os"
	"path"
	"runtime/debug"
	"strings"
)

func GetFileNodeTree(dstPath string, level int) (f model.File, err error) {
	return getFileNodeTree(dstPath, level, dstPath)
}

func getFileNodeTree(dstPath string, level int, rootPath string) (f model.File, err error) {
	dstF, err := os.Open(dstPath)
	if err != nil {
		log.Errorf(err.Error())
		debug.PrintStack()
		return f, err
	}
	defer dstF.Close()
	fileInfo, err := dstF.Stat()
	if err != nil {
		log.Errorf(err.Error())
		debug.PrintStack()
		return f, nil
	}
	if !fileInfo.IsDir() { //如果dstF是文件
		return model.File{
			Label:    fileInfo.Name(),
			Name:     fileInfo.Name(),
			Path:     strings.Replace(dstPath, rootPath, "", -1),
			IsDir:    false,
			Size:     fileInfo.Size(),
			Children: nil,
		}, nil
	} else { //如果dstF是文件夹
		dir, err := dstF.Readdir(0) //获取文件夹下各个文件或文件夹的fileInfo
		if err != nil {
			log.Errorf(err.Error())
			debug.PrintStack()
			return f, nil
		}
		f = model.File{
			Label:    path.Base(dstPath),
			Name:     path.Base(dstPath),
			Path:     strings.Replace(dstPath, rootPath, "", -1),
			IsDir:    true,
			Size:     0,
			Children: nil,
		}
		for _, subFileInfo := range dir {
			subFileNode, err := getFileNodeTree(path.Join(dstPath, subFileInfo.Name()), level+1, rootPath)
			if err != nil {
				log.Errorf(err.Error())
				debug.PrintStack()
				return f, err
			}
			f.Children = append(f.Children, subFileNode)
		}
		return f, nil
	}
}

你可能感兴趣的:(6月27实训报告)