Improve document extraction and including a document extraction command (#17183)
* Add extract documents content command * Adding the extraction command and making the pure go pdf library as secondary option * Improving the memory usage and docextractor interface * Enable content extraction by default in all the instances * Tiny improvement on archive indexing * Adding App interface generation and the opentracing layer * Fixing linter errors * Addressing PR review comments * Addressing PR review comments
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
75824257d5
Коммит
819e4c0c64
@@ -515,6 +515,7 @@ type AppIface interface {
|
||||
EnableUserAccessToken(token *model.UserAccessToken) *model.AppError
|
||||
EnvironmentConfig(filter func(reflect.StructField) bool) map[string]interface{}
|
||||
ExportPermissions(w io.Writer) error
|
||||
ExtractContentFromFileInfo(fileInfo *model.FileInfo) error
|
||||
FetchSamlMetadataFromIdp(url string) ([]byte, *model.AppError)
|
||||
FileBackend() (filestore.FileBackend, *model.AppError)
|
||||
FileExists(path string) (bool, *model.AppError)
|
||||
|
||||
58
app/file.go
58
app/file.go
@@ -9,7 +9,6 @@ import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
@@ -30,6 +29,7 @@ import (
|
||||
|
||||
"github.com/disintegration/imaging"
|
||||
_ "github.com/oov/psd"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rwcarlsen/goexif/exif"
|
||||
_ "golang.org/x/image/bmp"
|
||||
_ "golang.org/x/image/tiff"
|
||||
@@ -75,6 +75,7 @@ const (
|
||||
ImageThumbnailPixelWidth = 120
|
||||
ImageThumbnailPixelHeight = 100
|
||||
ImagePreviewPixelWidth = 1920
|
||||
MaxContentExtractionSize = 1024 * 1024 // 1Mb
|
||||
)
|
||||
|
||||
func (a *App) FileBackend() (filestore.FileBackend, *model.AppError) {
|
||||
@@ -773,21 +774,9 @@ func (a *App) UploadFileX(channelID, name string, input io.Reader,
|
||||
if *a.Config().FileSettings.ExtractContent && a.Config().FeatureFlags.FilesSearch {
|
||||
infoCopy := *t.fileinfo
|
||||
a.Srv().Go(func() {
|
||||
file, aerr := a.FileReader(t.fileinfo.Path)
|
||||
if aerr != nil {
|
||||
mlog.Error("Failed to open file for extract file content", mlog.Err(aerr))
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
text, err := docextractor.Extract(infoCopy.Name, file, docextractor.ExtractSettings{
|
||||
ArchiveRecursion: *a.Config().FileSettings.ArchiveRecursion,
|
||||
})
|
||||
err := a.ExtractContentFromFileInfo(&infoCopy)
|
||||
if err != nil {
|
||||
mlog.Error("Failed to extract file content", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
if storeErr := a.Srv().Store.FileInfo().SetContent(infoCopy.Id, text); storeErr != nil {
|
||||
mlog.Error("Failed to save the extracted file content", mlog.Err(storeErr))
|
||||
mlog.Error("Failed to extract file content", mlog.Err(err), mlog.String("fileInfoId", infoCopy.Id))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1043,21 +1032,9 @@ func (a *App) DoUploadFileExpectModification(now time.Time, rawTeamId string, ra
|
||||
if *a.Config().FileSettings.ExtractContent && a.Config().FeatureFlags.FilesSearch {
|
||||
infoCopy := *info
|
||||
a.Srv().Go(func() {
|
||||
file, aerr := a.FileReader(infoCopy.Path)
|
||||
if aerr != nil {
|
||||
mlog.Error("Failed to open file for extract file content", mlog.Err(aerr))
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
text, err := docextractor.Extract(infoCopy.Name, file, docextractor.ExtractSettings{
|
||||
ArchiveRecursion: *a.Config().FileSettings.ArchiveRecursion,
|
||||
})
|
||||
err := a.ExtractContentFromFileInfo(&infoCopy)
|
||||
if err != nil {
|
||||
mlog.Error("Failed to extract file content", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
if storeErr := a.Srv().Store.FileInfo().SetContent(infoCopy.Id, text); storeErr != nil {
|
||||
mlog.Error("Failed to save the extracted file content", mlog.Err(storeErr))
|
||||
mlog.Error("Failed to extract file content", mlog.Err(err), mlog.String("fileInfoId", infoCopy.Id))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1396,3 +1373,26 @@ func (a *App) SearchFilesInTeamForUser(terms string, userId string, teamId strin
|
||||
|
||||
return fileInfoSearchResults, nil
|
||||
}
|
||||
|
||||
func (a *App) ExtractContentFromFileInfo(fileInfo *model.FileInfo) error {
|
||||
file, aerr := a.FileReader(fileInfo.Path)
|
||||
if aerr != nil {
|
||||
return errors.Wrap(aerr, "failed to open file for extract file content")
|
||||
}
|
||||
defer file.Close()
|
||||
text, err := docextractor.Extract(fileInfo.Name, file, docextractor.ExtractSettings{
|
||||
ArchiveRecursion: *a.Config().FileSettings.ArchiveRecursion,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to extract file content")
|
||||
}
|
||||
if text != "" {
|
||||
if len(text) > MaxContentExtractionSize {
|
||||
text = text[0:MaxContentExtractionSize]
|
||||
}
|
||||
if storeErr := a.Srv().Store.FileInfo().SetContent(fileInfo.Id, text); storeErr != nil {
|
||||
return errors.Wrap(storeErr, "failed to save the extracted file content")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ const EmojisPermissionsMigrationKey = "EmojisPermissionsMigrationComplete"
|
||||
const GuestRolesCreationMigrationKey = "GuestRolesCreationMigrationComplete"
|
||||
const SystemConsoleRolesCreationMigrationKey = "SystemConsoleRolesCreationMigrationComplete"
|
||||
const ContentExtractionConfigMigrationKey = "ContentExtractionConfigMigrationComplete"
|
||||
const usersLimitToAutoEnableContentExtraction = 500
|
||||
|
||||
// This function migrates the default built in roles from code/config to the database.
|
||||
func (a *App) DoAdvancedPermissionsMigration() {
|
||||
@@ -285,35 +284,6 @@ func (a *App) DoSystemConsoleRolesCreationMigration() {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) doContentExtractionConfigMigration() {
|
||||
if !a.Config().FeatureFlags.FilesSearch {
|
||||
return
|
||||
}
|
||||
// If the migration is already marked as completed, don't do it again.
|
||||
if _, err := a.Srv().Store.System().GetByName(ContentExtractionConfigMigrationKey); err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if usersCount, err := a.Srv().Store.User().Count(model.UserCountOptions{}); err != nil {
|
||||
mlog.Critical("Failed to get the users count for migrating the content extraction, using default value", mlog.Err(err))
|
||||
} else {
|
||||
if usersCount < usersLimitToAutoEnableContentExtraction {
|
||||
a.UpdateConfig(func(config *model.Config) {
|
||||
config.FileSettings.ExtractContent = model.NewBool(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
system := model.System{
|
||||
Name: ContentExtractionConfigMigrationKey,
|
||||
Value: "true",
|
||||
}
|
||||
|
||||
if err := a.Srv().Store.System().Save(&system); err != nil {
|
||||
mlog.Critical("Failed to mark content extraction config migration as completed.", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) DoAppMigrations() {
|
||||
a.DoAdvancedPermissionsMigration()
|
||||
a.DoEmojisPermissionsMigration()
|
||||
@@ -325,5 +295,4 @@ func (a *App) DoAppMigrations() {
|
||||
if err != nil {
|
||||
mlog.Critical("(app.App).DoPermissionsMigrations failed", mlog.Err(err))
|
||||
}
|
||||
a.doContentExtractionConfigMigration()
|
||||
}
|
||||
|
||||
@@ -3795,6 +3795,28 @@ func (a *OpenTracingAppLayer) ExtendSessionExpiryIfNeeded(session *model.Session
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) ExtractContentFromFileInfo(fileInfo *model.FileInfo) error {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.ExtractContentFromFileInfo")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store.SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.ExtractContentFromFileInfo(fileInfo)
|
||||
|
||||
if resultVar0 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar0))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) FetchSamlMetadataFromIdp(url string) ([]byte, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.FetchSamlMetadataFromIdp")
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
"github.com/mattermost/mattermost-server/v5/plugin"
|
||||
"github.com/mattermost/mattermost-server/v5/services/docextractor"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/v5/store"
|
||||
)
|
||||
@@ -303,15 +302,9 @@ func (a *App) UploadData(us *model.UploadSession, rd io.Reader) (*model.FileInfo
|
||||
if *a.Config().FileSettings.ExtractContent && a.Config().FeatureFlags.FilesSearch {
|
||||
infoCopy := *info
|
||||
a.Srv().Go(func() {
|
||||
text, err := docextractor.Extract(infoCopy.Name, file, docextractor.ExtractSettings{
|
||||
ArchiveRecursion: *a.Config().FileSettings.ArchiveRecursion,
|
||||
})
|
||||
err := a.ExtractContentFromFileInfo(&infoCopy)
|
||||
if err != nil {
|
||||
mlog.Error("Failed to extract file content", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
if storeErr := a.Srv().Store.FileInfo().SetContent(infoCopy.Id, text); storeErr != nil {
|
||||
mlog.Error("Failed to save the extracted file content", mlog.Err(storeErr))
|
||||
mlog.Error("Failed to extract file content", mlog.Err(err), mlog.String("fileInfoId", infoCopy.Id))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user