Skip to content

Commit

Permalink
Добавлен помощник работы с файлами.
Browse files Browse the repository at this point in the history
  • Loading branch information
monoflash committed Jul 30, 2022
1 parent 43d8c4a commit ef14a58
Show file tree
Hide file tree
Showing 23 changed files with 9,394 additions and 56 deletions.
1 change: 1 addition & 0 deletions .gogenerate
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github.com/webnice/kit/v3/module/file
57 changes: 1 addition & 56 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ dep-dev: dep-init
## Кодогенерация (run only during development).
## All generating files are included in a .gogenerate file.
gen: dep-init
@for PKGNAME in $(GOGENERATE); do GO111MODULE="off" GOPATH="$(DIR)" DB2STRUCT_DRV="$(GOOSE_DRV_MYSQL)" DB2STRUCT_DSN="$(GOOSE_DSN_MYSQL)" go generate $${PKGNAME}; done
@for PKGNAME in $(GOGENERATE); do go generate $${PKGNAME}; done
.PHONY: gen

## Project building for environment architecture.
Expand Down Expand Up @@ -102,61 +102,6 @@ v:
## RPM build openSUSE linux version.
RPMBUILD_OS ?= $(RPMBUILD_OS:leap)
RPMBUILD_OS ?= $(RPMBUILD_OS:tumbleweed)
## Creating RPM package.
# rpm:
# ## Prepare for creating RPM package.
# @mkdir -p ${DIR}/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}; true
# ## Copying the content needed to build the RPM package
# ## File descriptions are contained in the .rpm file
# @for item in $(PROJECT_RPM_BUILD_SOURCE); do\
# SRC=`echo $${item} | awk -F':' '{print $$1}'`; \
# DST=`echo $${item} | awk -F':' '{print $$2}'`; \
# cp -v ${DIR}/$${SRC} ${DIR}/rpmbuild/$${DST}; \
# done
# ## Execution of data preparation commands for build an RPM package.
# ## Command descriptions are contained in the .rpm file.
# $(call PROJECT_RPM_BUILD)
# ## Updates SPEC changelog section, from git log information.
# @if command -v "changelogmaker"; then \
# mv ${DIR}/rpmbuild/SPECS/${APP}.spec ${DIR}/rpmbuild/SPECS/src.spec; \
# cd ${DIR}; changelogmaker -s ${DIR}/rpmbuild/SPECS/src.spec > ${DIR}/rpmbuild/SPECS/${APP}.spec; \
# fi
# ## Build the RPM package.
# @RPMBUILD_OS="${RPMBUILD_OS}" rpmbuild \
# --target x86_64 \
# --define "_topdir ${DIR}/rpmbuild" \
# --define "_app_version_number $(VERN01)" \
# --define "_app_version_build $(VERB01)" \
# -bb ${DIR}/rpmbuild/SPECS/${APP}.spec
# .PHONY: rpm

## Creating RPM package for i386 architecture.
# rpm-i386:
# ## Prepare for creating RPM package.
# @mkdir -p ${DIR}/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}; true
# ## Copying the content needed to build the RPM package.
# ## File descriptions are contained in the .rpm file.
# @for item in $(PROJECT_RPM_BUILD_SOURCE_I386); do\
# SRC=`echo $${item} | awk -F':' '{print $$1}'`; \
# DST=`echo $${item} | awk -F':' '{print $$2}'`; \
# cp -v ${DIR}/$${SRC} ${DIR}/rpmbuild/$${DST}; \
# done
# ## Execution of data preparation commands for build an RPM package.
# ## Command descriptions are contained in the .rpm file.
# $(call PROJECT_RPM_BUILD_I386)
# ## Updates SPEC changelog section, from git log information.
# @if command -v "changelogmaker"; then \
# mv ${DIR}/rpmbuild/SPECS/${APP}.spec ${DIR}/rpmbuild/SPECS/src.spec; \
# cd ${DIR}; changelogmaker -s ${DIR}/rpmbuild/SPECS/src.spec > ${DIR}/rpmbuild/SPECS/${APP}.spec; \
# fi
# ## Build the RPM package.
# @RPMBUILD_OS="${RPMBUILD_OS}" rpmbuild \
# --target i386 \
# --define "_topdir ${DIR}/rpmbuild" \
# --define "_app_version_number $(VERN01)" \
# --define "_app_version_build $(VERB01)" \
# -bb ${DIR}/rpmbuild/SPECS/${APP}.spec
# .PHONY: rpm-i386

## Migration tools for all databases.
## Please see files .env and .env_example, for setup access to databases.
Expand Down
43 changes: 43 additions & 0 deletions module/file/clean.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Package file
package file

import (
"fmt"
"io/ioutil"
"os"
"path"
)

// CleanEmptyFolder Удаление пустых папок.
func (fl *impl) CleanEmptyFolder(folderPath string) (err error) {
return fl.cleanEmptyFolderRecursive(folderPath, 0)
}

// Более удобный для рекурсии вариант функции удаления пустых папок.
func (fl *impl) cleanEmptyFolderRecursive(pt string, level int64) (err error) {
var (
fi []os.FileInfo
n int
)

if fi, err = ioutil.ReadDir(pt); err != nil {
err = fmt.Errorf("чтение директории %q прервано ошибкой: %w", pt, err)
return
}
if len(fi) == 0 && level > 0 {
if err = os.Remove(pt); err != nil {
err = fmt.Errorf("удаление директории %q прервано ошибкой: %w", pt, err)
}
return
}
for n = range fi {
switch {
case fi[n].IsDir():
if err = fl.cleanEmptyFolderRecursive(path.Join(pt, fi[n].Name()), level+1); err != nil {
return
}
}
}

return
}
90 changes: 90 additions & 0 deletions module/file/copy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Package file
package file

import (
"crypto/sha512"
"encoding/hex"
"fmt"
"hash"
"io"
"os"
)

// Copy Копирует один файл в другой.
func (fl *impl) Copy(dst string, src string) (size int64, err error) {
const defaultFileMode = os.FileMode(0644)
var (
inp *os.File
out *os.File
)

if inp, err = os.Open(src); err != nil {
err = fmt.Errorf("открытие файла %q прервано ошибкой: %w", src, err)
return
}
defer func() { _ = inp.Close() }()
if out, err = os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, defaultFileMode); err != nil {
err = fmt.Errorf("создание файла %q прервано ошибкой: %w", dst, err)
return
}
defer func() { _ = out.Sync(); _ = out.Close() }()
if size, err = io.Copy(out, inp); err != nil {
err = fmt.Errorf("копирование данных из файла %q в файл %q завершилось ошибкой: %w", dst, src, err)
return
}

return
}

// CopyWithSha512Sum Копирование контента с параллельным вычислением контрольной суммы алгоритмом SHA512.
func (fl *impl) CopyWithSha512Sum(dst io.Writer, src io.Reader) (written int64, sha512sum string, err error) {
var (
er, ew error
size, nr, nw int
buf []byte
lr *io.LimitedReader
ok bool
sha hash.Hash
end bool
)

size = 32 * 1024
if lr, ok = src.(*io.LimitedReader); ok && int64(size) > lr.N {
if size = int(lr.N); lr.N < 1 {
size = 1
}
}
buf, sha = make([]byte, size), sha512.New()
for {
if end {
break
}
if nr, er = src.Read(buf); nr > 0 {
if _, err = sha.Write(buf[0:nr]); err != nil {
err = fmt.Errorf("вычисление контрольной суммы sha-512 прервано ошибкой: %w", err)
return
}
if nw, ew = dst.Write(buf[0:nr]); nw > 0 {
written += int64(nw)
}
if ew != nil {
err = ew
break
}
if nr != nw {
err = io.ErrShortWrite
break
}
}
switch er {
case nil:
case io.EOF:
end, er = true, nil
default:
end, err = true, fmt.Errorf("чтение данных прервано ошибкой: %w", er)
}
}
sha512sum = hex.EncodeToString(sha.Sum(nil))

return
}
43 changes: 43 additions & 0 deletions module/file/file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Package file
package file

//go:generate go run mime_generate.go
//go:generate gofmt -w mime.go

import (
stdMime "mime"
"os"
"strings"
)

// Добавление всех mime types
func init() { mimeAddAll() }

// New creates new object and return Interface
func New() Interface {
var obj = new(impl)
return obj
}

// Добавление всех mime types
func mimeAddAll() {
const prefix = `.`
var mt string

for mt = range mimeTypeExtension {
_ = stdMime.AddExtensionType(prefix+mimeTypeExtension[mt], mt)
}
}

// GetFilename Выделение из полного пути к файлу, имени файла.
func (fl *impl) GetFilename(filename string) (ret string) {
var ch []string

if ch = strings.Split(filename, string(os.PathSeparator)); len(ch) > 0 {
ret = ch[len(ch)-1]
return
}
ret = filename

return
}
44 changes: 44 additions & 0 deletions module/file/info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Package file
package file

import (
"crypto/sha512"
"encoding/hex"
"fmt"
"hash"
"io"
"os"
)

// GetInfoSha512 Считывание информации о файле с контрольной суммой.
func (fl *impl) GetInfoSha512(filename string) (inf *InfoSha512, err error) {
const defaultFileMode = 0755
var (
fh *os.File
s512 hash.Hash
fi os.FileInfo
)

inf = new(InfoSha512)
if fh, err = os.OpenFile(filename, os.O_RDONLY, os.FileMode(defaultFileMode)); err != nil {
err = fmt.Errorf("open file %q, error: %s", filename, err)
return
}
defer func() { _ = fh.Close() }()
s512 = sha512.New()
if inf.Size, err = io.Copy(s512, fh); err != nil {
err = fmt.Errorf("чтение файла %q прервано ошибкой: %w", filename, err)
return
}
if fi, err = fh.Stat(); err != nil {
err = fmt.Errorf("получение атрибутов файла %q прервано ошибкой: %w", filename, err)
return
}
if inf.Size != fi.Size() {
err = fmt.Errorf("считан файл размером %d байт, ожидался размер %d байт", fi.Size(), inf.Size)
return
}
inf.Sha512, inf.Name = hex.EncodeToString(s512.Sum(nil)[:]), fl.GetFilename(filename)

return
}
43 changes: 43 additions & 0 deletions module/file/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Package file
package file

import (
"io/ioutil"
"os"
"path"
)

// RecursiveFileList Поиск всех файлов начиная от folderPath рекурсивно.
// Возвращается слайс относительных имён файлов.
func (fl *impl) RecursiveFileList(folderPath string) (ret []string, err error) {
ret, err = fl.recursiveFileListLoop(folderPath, "")

return
}

// Удобная для рекурсии функция
func (fl *impl) recursiveFileListLoop(baseFolderPath, currentFolderPath string) (ret []string, err error) {
var (
pf string
fis []os.FileInfo
n int
)

pf = path.Join(baseFolderPath, currentFolderPath)
if fis, err = ioutil.ReadDir(pf); err != nil {
return
}
for n = range fis {
switch {
case fis[n].IsDir():
ret, err = fl.recursiveFileListLoop(baseFolderPath, path.Join(currentFolderPath, fis[n].Name()))
if err != nil {
return
}
case !fis[n].IsDir():
ret = append(ret, path.Join(currentFolderPath, fis[n].Name()))
}
}

return
}
32 changes: 32 additions & 0 deletions module/file/load.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Package file
package file

import (
"bytes"
"fmt"
"io"
"os"
)

// LoadFile Загрузка файла в память и возврат в виде *bytes.Buffer.
func (fl *impl) LoadFile(filename string) (data *bytes.Buffer, info os.FileInfo, err error) {
const defaultFileMode = os.FileMode(0755)
var fh *os.File

if fh, err = os.OpenFile(filename, os.O_RDONLY, defaultFileMode); err != nil {
err = fmt.Errorf("открытие файла %q прервано ошибкой: %w", filename, err)
return
}
defer func() { _ = fh.Close() }()
if info, err = fh.Stat(); err != nil {
err = fmt.Errorf("получение атрибутов файла %q прервано ошибкой: %w", filename, err)
return
}
data = new(bytes.Buffer)
if _, err = io.Copy(data, fh); err != nil {
err = fmt.Errorf("чтение данных из файла %q прервано ошибкой: %w", filename, err)
return
}

return
}
Loading

0 comments on commit ef14a58

Please sign in to comment.