2014-10-28 19:04:46 +03:00
|
|
|
|
// File tree manipulations.
|
2014-08-18 17:45:43 +04:00
|
|
|
|
package file
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"io/ioutil"
|
|
|
|
|
"net/http"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
2014-09-12 06:56:18 +04:00
|
|
|
|
"runtime"
|
2014-08-18 17:45:43 +04:00
|
|
|
|
"sort"
|
|
|
|
|
"strings"
|
2014-09-07 13:31:57 +04:00
|
|
|
|
|
|
|
|
|
"github.com/b3log/wide/conf"
|
2014-10-28 19:04:46 +03:00
|
|
|
|
"github.com/b3log/wide/event"
|
2014-09-17 10:35:48 +04:00
|
|
|
|
"github.com/b3log/wide/session"
|
2014-09-07 13:31:57 +04:00
|
|
|
|
"github.com/b3log/wide/util"
|
|
|
|
|
"github.com/golang/glog"
|
2014-08-18 17:45:43 +04:00
|
|
|
|
)
|
|
|
|
|
|
2014-10-28 19:04:46 +03:00
|
|
|
|
// File node, used to construct the file tree.
|
2014-10-11 10:08:29 +04:00
|
|
|
|
type FileNode struct {
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
Path string `json:"path"`
|
2014-10-28 19:04:46 +03:00
|
|
|
|
IconSkin string `json:"iconSkin"` // Value should be end with a space
|
|
|
|
|
Type string `json:"type"` // "f": file,"d": directory
|
2014-10-11 10:08:29 +04:00
|
|
|
|
Mode string `json:"mode"`
|
|
|
|
|
FileNodes []*FileNode `json:"children"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 代码片段. 这个结构可用于“查找使用”、“文件搜索”等的返回值.
|
|
|
|
|
type Snippet struct {
|
|
|
|
|
Path string `json:"path"` // 文件路径
|
|
|
|
|
Line int `json:"line"` // 行号
|
|
|
|
|
Ch int `json:"ch"` // 列号
|
|
|
|
|
Contents []string `json:"contents"` // 附近几行
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-12 06:56:18 +04:00
|
|
|
|
// 构造用户工作空间文件树.
|
2014-09-25 13:34:05 +04:00
|
|
|
|
//
|
2014-09-12 06:56:18 +04:00
|
|
|
|
// 将 Go API 源码包($GOROOT/src/pkg)也作为子节点,这样能方便用户查看 Go API 源码.
|
2014-08-18 17:45:43 +04:00
|
|
|
|
func GetFiles(w http.ResponseWriter, r *http.Request) {
|
2014-09-01 16:50:51 +04:00
|
|
|
|
data := map[string]interface{}{"succ": true}
|
|
|
|
|
defer util.RetJSON(w, r, data)
|
|
|
|
|
|
2014-09-17 10:35:48 +04:00
|
|
|
|
session, _ := session.HTTPSession.Get(r, "wide-session")
|
2014-08-31 10:15:13 +04:00
|
|
|
|
|
2014-08-31 14:50:38 +04:00
|
|
|
|
username := session.Values["username"].(string)
|
2014-10-26 09:46:15 +03:00
|
|
|
|
userWorkspace := conf.Wide.GetUserWorkspace(username)
|
2014-10-28 08:22:39 +03:00
|
|
|
|
workspaces := filepath.SplitList(userWorkspace)
|
2014-08-31 10:15:13 +04:00
|
|
|
|
|
2014-10-26 09:46:15 +03:00
|
|
|
|
root := FileNode{Name: "root", Path: "", IconSkin: "ico-ztree-dir ", Type: "d", FileNodes: []*FileNode{}}
|
|
|
|
|
|
|
|
|
|
// 工作空间节点处理
|
|
|
|
|
for _, workspace := range workspaces {
|
|
|
|
|
workspacePath := workspace + conf.PathSeparator + "src"
|
|
|
|
|
|
|
|
|
|
workspaceNode := FileNode{Name: workspace[strings.LastIndex(workspace, conf.PathSeparator)+1:] + " (" +
|
|
|
|
|
workspace + ")",
|
|
|
|
|
Path: workspacePath, IconSkin: "ico-ztree-dir ", Type: "d", FileNodes: []*FileNode{}}
|
|
|
|
|
|
|
|
|
|
walk(workspacePath, &workspaceNode)
|
|
|
|
|
|
|
|
|
|
// 添加工作空间节点
|
|
|
|
|
root.FileNodes = append(root.FileNodes, &workspaceNode)
|
|
|
|
|
}
|
2014-08-18 17:45:43 +04:00
|
|
|
|
|
2014-09-12 06:56:18 +04:00
|
|
|
|
// 构造 Go API 节点
|
2014-10-13 10:34:42 +04:00
|
|
|
|
apiPath := runtime.GOROOT() + conf.PathSeparator + "src" + conf.PathSeparator + "pkg"
|
2014-09-12 06:56:18 +04:00
|
|
|
|
apiNode := FileNode{Name: "Go API", Path: apiPath, FileNodes: []*FileNode{}}
|
2014-09-16 17:20:12 +04:00
|
|
|
|
|
|
|
|
|
goapiBuildOKSignal := make(chan bool)
|
2014-09-12 06:56:18 +04:00
|
|
|
|
go func() {
|
2014-09-16 17:20:12 +04:00
|
|
|
|
apiNode.Type = "d"
|
|
|
|
|
// TOOD: Go API 用另外的样式
|
|
|
|
|
apiNode.IconSkin = "ico-ztree-dir "
|
|
|
|
|
|
|
|
|
|
walk(apiPath, &apiNode)
|
|
|
|
|
|
|
|
|
|
// 放行信号
|
|
|
|
|
close(goapiBuildOKSignal)
|
2014-09-12 06:56:18 +04:00
|
|
|
|
}()
|
|
|
|
|
|
2014-09-16 17:20:12 +04:00
|
|
|
|
// 等待放行
|
|
|
|
|
<-goapiBuildOKSignal
|
|
|
|
|
|
2014-09-12 06:56:18 +04:00
|
|
|
|
// 添加 Go API 节点
|
|
|
|
|
root.FileNodes = append(root.FileNodes, &apiNode)
|
2014-08-18 17:45:43 +04:00
|
|
|
|
|
|
|
|
|
data["root"] = root
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-12 13:10:58 +04:00
|
|
|
|
// 编辑器打开一个文件.
|
2014-08-18 17:45:43 +04:00
|
|
|
|
func GetFile(w http.ResponseWriter, r *http.Request) {
|
2014-09-01 14:55:11 +04:00
|
|
|
|
data := map[string]interface{}{"succ": true}
|
2014-09-01 16:50:51 +04:00
|
|
|
|
defer util.RetJSON(w, r, data)
|
2014-09-01 14:55:11 +04:00
|
|
|
|
|
2014-08-18 17:45:43 +04:00
|
|
|
|
var args map[string]interface{}
|
|
|
|
|
|
2014-10-28 16:32:19 +03:00
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
|
2014-08-18 17:45:43 +04:00
|
|
|
|
glog.Error(err)
|
2014-09-01 16:50:51 +04:00
|
|
|
|
data["succ"] = false
|
2014-08-18 17:45:43 +04:00
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
path := args["path"].(string)
|
|
|
|
|
buf, _ := ioutil.ReadFile(path)
|
|
|
|
|
|
2014-09-05 17:10:28 +04:00
|
|
|
|
extension := filepath.Ext(path)
|
2014-09-01 17:40:10 +04:00
|
|
|
|
|
|
|
|
|
// 通过文件扩展名判断是否是图片文件(图片在浏览器里新建 tab 打开)
|
|
|
|
|
if isImg(extension) {
|
|
|
|
|
data["mode"] = "img"
|
|
|
|
|
|
|
|
|
|
path2 := strings.Replace(path, "\\", "/", -1)
|
2014-09-05 17:10:28 +04:00
|
|
|
|
idx := strings.Index(path2, "/data/user_workspaces")
|
2014-09-01 17:40:10 +04:00
|
|
|
|
data["path"] = path2[idx:]
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-01 14:55:11 +04:00
|
|
|
|
isBinary := false
|
2014-09-01 17:40:10 +04:00
|
|
|
|
// 判断是否是其他二进制文件
|
2014-09-01 14:55:11 +04:00
|
|
|
|
for _, b := range buf {
|
|
|
|
|
if 0 == b { // 包含 0 字节就认为是二进制文件
|
|
|
|
|
isBinary = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if isBinary {
|
|
|
|
|
// 是二进制文件的话前端编辑器不打开
|
|
|
|
|
data["succ"] = false
|
|
|
|
|
data["msg"] = "Can't open a binary file :("
|
|
|
|
|
} else {
|
|
|
|
|
data["content"] = string(buf)
|
|
|
|
|
data["mode"] = getEditorMode(extension)
|
2014-09-13 09:05:50 +04:00
|
|
|
|
data["path"] = path
|
2014-08-18 17:45:43 +04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-12 13:10:58 +04:00
|
|
|
|
// 保存文件.
|
2014-08-18 17:45:43 +04:00
|
|
|
|
func SaveFile(w http.ResponseWriter, r *http.Request) {
|
2014-09-01 16:50:51 +04:00
|
|
|
|
data := map[string]interface{}{"succ": true}
|
|
|
|
|
defer util.RetJSON(w, r, data)
|
|
|
|
|
|
2014-08-18 17:45:43 +04:00
|
|
|
|
var args map[string]interface{}
|
|
|
|
|
|
2014-10-28 16:32:19 +03:00
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
|
2014-08-18 17:45:43 +04:00
|
|
|
|
glog.Error(err)
|
2014-09-01 16:50:51 +04:00
|
|
|
|
data["succ"] = false
|
2014-08-18 17:45:43 +04:00
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
filePath := args["file"].(string)
|
|
|
|
|
|
|
|
|
|
fout, err := os.Create(filePath)
|
|
|
|
|
|
|
|
|
|
if nil != err {
|
|
|
|
|
glog.Error(err)
|
2014-09-01 16:50:51 +04:00
|
|
|
|
data["succ"] = false
|
2014-08-18 17:45:43 +04:00
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
code := args["code"].(string)
|
|
|
|
|
|
|
|
|
|
fout.WriteString(code)
|
|
|
|
|
|
|
|
|
|
if err := fout.Close(); nil != err {
|
|
|
|
|
glog.Error(err)
|
2014-09-01 16:50:51 +04:00
|
|
|
|
data["succ"] = false
|
2014-08-18 17:45:43 +04:00
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-28 19:04:46 +03:00
|
|
|
|
// NewFile handles request of creating file or directory.
|
2014-08-18 17:45:43 +04:00
|
|
|
|
func NewFile(w http.ResponseWriter, r *http.Request) {
|
2014-09-01 16:50:51 +04:00
|
|
|
|
data := map[string]interface{}{"succ": true}
|
|
|
|
|
defer util.RetJSON(w, r, data)
|
|
|
|
|
|
2014-08-18 17:45:43 +04:00
|
|
|
|
var args map[string]interface{}
|
|
|
|
|
|
2014-10-28 16:32:19 +03:00
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
|
2014-08-18 17:45:43 +04:00
|
|
|
|
glog.Error(err)
|
2014-09-01 16:50:51 +04:00
|
|
|
|
data["succ"] = false
|
2014-08-18 17:45:43 +04:00
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
path := args["path"].(string)
|
|
|
|
|
fileType := args["fileType"].(string)
|
2014-10-28 19:04:46 +03:00
|
|
|
|
sid := args["sid"].(string)
|
|
|
|
|
|
|
|
|
|
wSession := session.WideSessions.Get(sid)
|
2014-08-18 17:45:43 +04:00
|
|
|
|
|
|
|
|
|
if !createFile(path, fileType) {
|
|
|
|
|
data["succ"] = false
|
2014-09-22 10:20:17 +04:00
|
|
|
|
|
2014-10-28 19:04:46 +03:00
|
|
|
|
wSession.EventQueue.Queue <- &event.Event{Code: event.EvtCodeServerInternalError, Sid: sid,
|
|
|
|
|
Data: "can't create file " + path}
|
|
|
|
|
|
2014-09-22 10:20:17 +04:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if "f" == fileType {
|
|
|
|
|
extension := filepath.Ext(path)
|
|
|
|
|
data["mode"] = getEditorMode(extension)
|
2014-08-18 17:45:43 +04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-28 19:04:46 +03:00
|
|
|
|
// RemoveFile handles request of removing file or directory.
|
2014-08-18 17:45:43 +04:00
|
|
|
|
func RemoveFile(w http.ResponseWriter, r *http.Request) {
|
2014-09-01 16:50:51 +04:00
|
|
|
|
data := map[string]interface{}{"succ": true}
|
|
|
|
|
defer util.RetJSON(w, r, data)
|
|
|
|
|
|
2014-08-18 17:45:43 +04:00
|
|
|
|
var args map[string]interface{}
|
|
|
|
|
|
2014-10-28 16:32:19 +03:00
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
|
2014-08-18 17:45:43 +04:00
|
|
|
|
glog.Error(err)
|
2014-09-01 16:50:51 +04:00
|
|
|
|
data["succ"] = false
|
2014-08-18 17:45:43 +04:00
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
path := args["path"].(string)
|
|
|
|
|
|
|
|
|
|
if !removeFile(path) {
|
|
|
|
|
data["succ"] = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-28 19:04:46 +03:00
|
|
|
|
// SearchText handles request of searching files under the specified directory with the specified keyword.
|
2014-10-11 10:59:38 +04:00
|
|
|
|
func SearchText(w http.ResponseWriter, r *http.Request) {
|
2014-10-11 10:08:29 +04:00
|
|
|
|
data := map[string]interface{}{"succ": true}
|
|
|
|
|
defer util.RetJSON(w, r, data)
|
|
|
|
|
|
|
|
|
|
var args map[string]interface{}
|
|
|
|
|
|
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
|
|
|
|
|
glog.Error(err)
|
|
|
|
|
data["succ"] = false
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dir := args["dir"].(string)
|
2014-10-11 10:59:38 +04:00
|
|
|
|
extension := args["extension"].(string)
|
|
|
|
|
text := args["text"].(string)
|
2014-10-11 10:08:29 +04:00
|
|
|
|
|
2014-10-11 10:59:38 +04:00
|
|
|
|
founds := search(dir, extension, text, []*Snippet{})
|
2014-10-11 10:08:29 +04:00
|
|
|
|
|
|
|
|
|
data["founds"] = founds
|
2014-08-18 17:45:43 +04:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-12 13:10:58 +04:00
|
|
|
|
// 遍历指定的路径,构造文件树.
|
2014-09-12 06:56:18 +04:00
|
|
|
|
func walk(path string, node *FileNode) {
|
2014-08-18 17:45:43 +04:00
|
|
|
|
files := listFiles(path)
|
|
|
|
|
|
|
|
|
|
for _, filename := range files {
|
|
|
|
|
fpath := filepath.Join(path, filename)
|
|
|
|
|
|
|
|
|
|
fio, _ := os.Lstat(fpath)
|
|
|
|
|
|
|
|
|
|
child := FileNode{Name: filename, Path: fpath, FileNodes: []*FileNode{}}
|
|
|
|
|
node.FileNodes = append(node.FileNodes, &child)
|
|
|
|
|
|
|
|
|
|
if nil == fio {
|
|
|
|
|
glog.Warningf("Path [%s] is nil", fpath)
|
|
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if fio.IsDir() {
|
|
|
|
|
child.Type = "d"
|
2014-09-05 13:39:21 +04:00
|
|
|
|
child.IconSkin = "ico-ztree-dir "
|
2014-08-23 19:06:23 +04:00
|
|
|
|
|
2014-09-12 06:56:18 +04:00
|
|
|
|
walk(fpath, &child)
|
2014-08-18 17:45:43 +04:00
|
|
|
|
} else {
|
|
|
|
|
child.Type = "f"
|
2014-09-05 17:10:28 +04:00
|
|
|
|
ext := filepath.Ext(fpath)
|
2014-09-05 13:01:20 +04:00
|
|
|
|
|
2014-09-05 17:10:28 +04:00
|
|
|
|
child.IconSkin = getIconSkin(ext)
|
2014-09-07 13:31:57 +04:00
|
|
|
|
child.Mode = getEditorMode(ext)
|
2014-08-18 17:45:43 +04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-25 13:34:05 +04:00
|
|
|
|
// 列出 dirname 指定目录下的文件/目录名.
|
2014-08-18 17:45:43 +04:00
|
|
|
|
func listFiles(dirname string) []string {
|
|
|
|
|
f, _ := os.Open(dirname)
|
|
|
|
|
|
|
|
|
|
names, _ := f.Readdirnames(-1)
|
|
|
|
|
f.Close()
|
|
|
|
|
|
|
|
|
|
sort.Strings(names)
|
|
|
|
|
|
2014-08-23 19:06:23 +04:00
|
|
|
|
dirs := []string{}
|
|
|
|
|
files := []string{}
|
|
|
|
|
|
2014-08-31 10:07:35 +04:00
|
|
|
|
// 排序:目录靠前,文件靠后
|
2014-08-23 19:06:23 +04:00
|
|
|
|
for _, name := range names {
|
|
|
|
|
fio, _ := os.Lstat(filepath.Join(dirname, name))
|
|
|
|
|
|
|
|
|
|
if fio.IsDir() {
|
2014-08-31 10:07:35 +04:00
|
|
|
|
// 排除 .git 目录
|
|
|
|
|
if ".git" == fio.Name() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-23 19:06:23 +04:00
|
|
|
|
dirs = append(dirs, name)
|
|
|
|
|
} else {
|
|
|
|
|
files = append(files, name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return append(dirs, files...)
|
2014-08-18 17:45:43 +04:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-25 13:34:05 +04:00
|
|
|
|
// 根据文件后缀获取文件树图标 CSS 类名.
|
|
|
|
|
//
|
|
|
|
|
// CSS 类名可参考 zTree 文档.
|
2014-09-05 13:01:20 +04:00
|
|
|
|
func getIconSkin(filenameExtension string) string {
|
2014-09-16 13:54:50 +04:00
|
|
|
|
if isImg(filenameExtension) {
|
|
|
|
|
return "ico-ztree-img "
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 13:01:20 +04:00
|
|
|
|
switch filenameExtension {
|
2014-09-16 13:54:50 +04:00
|
|
|
|
case ".html", ".htm":
|
|
|
|
|
return "ico-ztree-html "
|
|
|
|
|
case ".go":
|
|
|
|
|
return "ico-ztree-go "
|
|
|
|
|
case ".css":
|
|
|
|
|
return "ico-ztree-css "
|
2014-09-05 13:01:20 +04:00
|
|
|
|
case ".txt":
|
2014-09-05 13:39:21 +04:00
|
|
|
|
return "ico-ztree-text "
|
2014-09-16 13:54:50 +04:00
|
|
|
|
case ".sql":
|
|
|
|
|
return "ico-ztree-sql "
|
2014-09-05 13:01:20 +04:00
|
|
|
|
case ".properties":
|
2014-09-05 13:39:21 +04:00
|
|
|
|
return "ico-ztree-pro "
|
2014-09-16 13:54:50 +04:00
|
|
|
|
case ".md":
|
|
|
|
|
return "ico-ztree-md "
|
2014-09-20 09:21:38 +04:00
|
|
|
|
case ".js", ".json":
|
2014-09-16 13:54:50 +04:00
|
|
|
|
return "ico-ztree-js "
|
|
|
|
|
case ".xml":
|
|
|
|
|
return "ico-ztree-xml "
|
2014-09-05 13:01:20 +04:00
|
|
|
|
default:
|
2014-09-16 13:54:50 +04:00
|
|
|
|
return "ico-ztree-other "
|
2014-09-05 13:01:20 +04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-25 13:34:05 +04:00
|
|
|
|
// 根据文件后缀获取编辑器 mode.
|
|
|
|
|
//
|
|
|
|
|
// 编辑器 mode 可参考 CodeMirror 文档.
|
2014-08-18 17:45:43 +04:00
|
|
|
|
func getEditorMode(filenameExtension string) string {
|
|
|
|
|
switch filenameExtension {
|
|
|
|
|
case ".go":
|
2014-09-06 18:33:09 +04:00
|
|
|
|
return "text/x-go"
|
2014-08-18 17:45:43 +04:00
|
|
|
|
case ".html":
|
2014-09-06 18:33:09 +04:00
|
|
|
|
return "text/html"
|
2014-08-18 17:45:43 +04:00
|
|
|
|
case ".md":
|
2014-09-06 18:33:09 +04:00
|
|
|
|
return "text/x-markdown"
|
2014-09-06 18:20:10 +04:00
|
|
|
|
case ".js":
|
2014-09-06 18:33:09 +04:00
|
|
|
|
return "text/javascript"
|
2014-09-06 18:20:10 +04:00
|
|
|
|
case ".json":
|
|
|
|
|
return "application/json"
|
2014-08-18 17:45:43 +04:00
|
|
|
|
case ".css":
|
2014-09-06 18:33:09 +04:00
|
|
|
|
return "text/css"
|
2014-08-18 17:45:43 +04:00
|
|
|
|
case ".xml":
|
2014-09-06 18:33:09 +04:00
|
|
|
|
return "application/xml"
|
2014-08-18 17:45:43 +04:00
|
|
|
|
case ".sh":
|
2014-09-06 18:33:09 +04:00
|
|
|
|
return "text/x-sh"
|
2014-08-18 17:45:43 +04:00
|
|
|
|
case ".sql":
|
2014-09-06 18:33:09 +04:00
|
|
|
|
return "text/x-sql"
|
2014-08-18 17:45:43 +04:00
|
|
|
|
default:
|
2014-09-06 18:33:09 +04:00
|
|
|
|
return "text/plain"
|
2014-08-18 17:45:43 +04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-25 13:34:05 +04:00
|
|
|
|
// 在 path 指定的路径上创建文件.
|
|
|
|
|
//
|
|
|
|
|
// fileType:
|
|
|
|
|
//
|
2014-10-08 09:54:16 +04:00
|
|
|
|
// "f": 文件
|
|
|
|
|
// "d": 目录
|
2014-08-18 17:45:43 +04:00
|
|
|
|
func createFile(path, fileType string) bool {
|
|
|
|
|
switch fileType {
|
|
|
|
|
case "f":
|
|
|
|
|
file, err := os.OpenFile(path, os.O_CREATE, 0664)
|
|
|
|
|
if nil != err {
|
2014-10-26 10:59:12 +03:00
|
|
|
|
glog.Error(err)
|
2014-08-18 17:45:43 +04:00
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer file.Close()
|
|
|
|
|
|
2014-10-26 10:59:12 +03:00
|
|
|
|
glog.V(5).Infof("Created file [%s]", path)
|
2014-08-18 17:45:43 +04:00
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
case "d":
|
|
|
|
|
err := os.Mkdir(path, 0775)
|
|
|
|
|
|
|
|
|
|
if nil != err {
|
2014-10-26 10:59:12 +03:00
|
|
|
|
glog.Error(err)
|
|
|
|
|
|
|
|
|
|
return false
|
2014-08-18 17:45:43 +04:00
|
|
|
|
}
|
|
|
|
|
|
2014-10-26 10:59:12 +03:00
|
|
|
|
glog.V(5).Infof("Created directory [%s]", path)
|
2014-08-18 17:45:43 +04:00
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
default:
|
2014-10-26 10:59:12 +03:00
|
|
|
|
glog.Errorf("Unsupported file type [%s]", fileType)
|
2014-08-18 17:45:43 +04:00
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-25 13:34:05 +04:00
|
|
|
|
// 删除 path 指定路径的文件或目录.
|
2014-08-18 17:45:43 +04:00
|
|
|
|
func removeFile(path string) bool {
|
|
|
|
|
if err := os.RemoveAll(path); nil != err {
|
|
|
|
|
glog.Errorf("Removes [%s] failed: [%s]", path, err.Error())
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glog.Infof("Removed [%s]", path)
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
}
|
2014-09-01 17:40:10 +04:00
|
|
|
|
|
2014-10-11 10:08:29 +04:00
|
|
|
|
// 在 dir 指定的目录(包含子目录)中的 extension 指定后缀的文件中搜索包含 text 文本的文件,类似 grep/findstr 命令.
|
|
|
|
|
func search(dir, extension, text string, snippets []*Snippet) []*Snippet {
|
2014-10-13 10:34:42 +04:00
|
|
|
|
if !strings.HasSuffix(dir, conf.PathSeparator) {
|
|
|
|
|
dir += conf.PathSeparator
|
2014-10-11 11:29:21 +04:00
|
|
|
|
}
|
|
|
|
|
|
2014-10-11 10:08:29 +04:00
|
|
|
|
f, _ := os.Open(dir)
|
2014-10-11 10:45:44 +04:00
|
|
|
|
fileInfos, err := f.Readdir(-1)
|
2014-10-11 10:08:29 +04:00
|
|
|
|
f.Close()
|
|
|
|
|
|
|
|
|
|
if nil != err {
|
|
|
|
|
glog.Errorf("Read dir [%s] failed: [%s]", dir, err.Error())
|
|
|
|
|
|
|
|
|
|
return snippets
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-11 10:45:44 +04:00
|
|
|
|
for _, fileInfo := range fileInfos {
|
2014-10-11 11:29:21 +04:00
|
|
|
|
path := dir + fileInfo.Name()
|
2014-10-11 10:45:44 +04:00
|
|
|
|
|
2014-10-11 11:29:21 +04:00
|
|
|
|
if fileInfo.IsDir() {
|
|
|
|
|
// 进入目录递归
|
|
|
|
|
snippets = search(path, extension, text, snippets)
|
|
|
|
|
} else if strings.HasSuffix(path, extension) {
|
|
|
|
|
// 在文件中进行搜索
|
2014-10-11 10:45:44 +04:00
|
|
|
|
ss := searchInFile(path, text)
|
|
|
|
|
|
|
|
|
|
snippets = append(snippets, ss...)
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-10-11 10:08:29 +04:00
|
|
|
|
|
|
|
|
|
return snippets
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-11 10:45:44 +04:00
|
|
|
|
// 在 path 指定的文件内容中搜索 text 指定的文本.
|
|
|
|
|
func searchInFile(path string, text string) []*Snippet {
|
|
|
|
|
ret := []*Snippet{}
|
|
|
|
|
|
|
|
|
|
bytes, err := ioutil.ReadFile(path)
|
|
|
|
|
if nil != err {
|
|
|
|
|
glog.Errorf("Read file [%s] failed: [%s]", path, err.Error())
|
|
|
|
|
|
|
|
|
|
return ret
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
content := string(bytes)
|
|
|
|
|
lines := strings.Split(content, "\n")
|
|
|
|
|
|
|
|
|
|
for idx, line := range lines {
|
|
|
|
|
ch := strings.Index(line, text)
|
|
|
|
|
|
|
|
|
|
if -1 != ch {
|
|
|
|
|
snippet := &Snippet{Path: path, Line: idx + 1, Ch: ch + 1, Contents: []string{line}}
|
|
|
|
|
|
|
|
|
|
ret = append(ret, snippet)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-25 13:34:05 +04:00
|
|
|
|
// 根据文件名后缀判断是否是图片文件.
|
2014-09-01 17:40:10 +04:00
|
|
|
|
func isImg(extension string) bool {
|
|
|
|
|
ext := strings.ToLower(extension)
|
|
|
|
|
|
|
|
|
|
switch ext {
|
|
|
|
|
case ".jpg", ".jpeg", ".bmp", ".gif", ".png", ".svg", ".ico":
|
|
|
|
|
return true
|
|
|
|
|
default:
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|