Доброго времени суток.

Делал интеграцию с хранилищем SharePoint(далее SP). А именно реализовал простой экспорт и импорт файлов.

На деталь "Файлы и ссылки" добавляется файл. Он сразу же уходит в SP.

В своём классе реализовал два метода ReadAsync и WriteAsync интерфейса IFileContentStorage.

Всё работает. Файлы экспортируются в SP и импортируются. Но есть проблема с офисными файлами.

А именно с xlsx, xls, docx, doc, pptx. Возможно проблемы и с офисными файлами других расширений. Не проверял.

Файлы с этими расширениями уходят в SP. Но если их обратно прочитать, то они уже приходят битыми.

То есть при открытии появляется следующее сообщение:

Ошибка в части содержимого в книге. Выполнить попытку восстановления? Если вы доверяете источнику, из которого получена книга, нажмите кнопку "Да". 

Если нажать "Да", то открывается страница Excel или Word с сообщением, что "Excel или Word удалось открыть файл, восстановив или удалив нечитаемое содержимое".

После нажатия "Да", файл может быть полностью восстановлен, а может открыться пустым. Если выгрузить файлы, которые пришли в SP, напрямую из хранилища(нажать "Download" на странице SP), то они открываются без ошибок.

С файлами других расширений подобных проблем нет. Графические файлы, текстовые, pdf - всё отлично.

Для реализации импорта и экспорта пробовал использовать HttpWebRequest,  RestClient, HttpClient. Результат один и тот же.

Для эксперимента создал консольное приложение, где применил те же способы экспорт и импорта. Только добавил сохранение полученного из SP значения Stream в файл на диск. Файл открылся без ошибок.

Не пойму, почему при реализации метода ReadAsync, который возвращает Task, появляются подобные проблемы. Может кто-то реализовывал подобное и тоже сталкивался с такими ошибками?

 

Нравится

1 комментарий

Добрый день!

Боюсь, что не сможем помочь с данной проблемой так как мы не работаем с хранилищем SharePoint и поэтому не знаем его особенностей.

Логично предположить, что проблема возникает именно при экспорте из SP. Возможно нарушился поток передачи и как результат файл пришёл неполный.

На маркетплейсе есть коннектор к SP от сторонних разработчиков и там можно найти небольшую документацию по реализации, возможно она вам поможет.

Показать все комментарии

1. Во вкладке "Файлы и примечания" необходимо Панель инструментов перенести вниз. Прилагаю изображения "как есть" и "как нужно".

 

2. Если файл/ таблица, размещенная там большая по размеру, то появляется прокручивание. Можно ли отображать все полностью без прокручивания? ( Прилагаю изображения "как есть" )

Нравится

2 комментария

Этот редактор реализован на стороннем компоненте CKEditor.

Согласно его документации, для переключения панели вниз нужно изменить одно свойство с top на bottom:

config.toolbarLocation = 'bottom';

Этот компонент реализован в js-файле ядра, как он меняется, написано тут. Но тогда поменяется всюду по системе, да и правки ядра не всегда возможны и не очень правильны.

По прокрутке аналогично, тут и тут, это задаётся в CSS.

Лучше будет переопределить свойства на уровне схемы в конфигурации.

 

Зверев Александр,

Спасибо большое! Попробуем воспользоваться) 

Показать все комментарии

Мне с удаленного сервера  приходит файл, который я хочу сохранить в creatio, каким образом это реализовать и куда сохранять, когда получу его?

Нравится

1 комментарий

Дмитрий, где именно Вы хотите это выполнять?

Если извне, то есть стандартный FileApiService, примеры работы с ним см. тут. Перед этим нужно ещё выполнить авторизацию.

 

Если же Вы хотите это делать это в серверной логике 7.Х, то нужно делать примерно то, что делается внутри этого сервиса, писать данные в поле Data записи о файле в таблице ...File от нужного раздела. См. пояснение в комментарии тут

Показать все комментарии

Добрый день!

Считаю, что была бы полезной функция наглядного наличия вложенного файла в Обращении,

например, такого вида:

Изображение удалено.

1 комментарий

Если реализовывать самостоятельно такую логику, проще всего будет доработать присвоение номера при создании обращения или добавить его изменение. Скрепку можно выводить как символ: ?.Только учесть, чтобы не сломалась привязка писем по номеру обращения в них.

Или, чтобы не трогать номер, вывести в реестр ещё одну текстовую колонку, куда и писать при помощи БП эту скрепу. Но тогда не будет видно в справочном поле со ссылкой на обращение, а только в реестре раздела или детали.

Показать все комментарии

Доброго времени суток.

Желательно присутствие бывалых Одинов, которые повидали многое в данном продукте.

В продолжение предыдущего поста было принято решения не отходить от стандартной реализации отправления файла, используя FileAPI.



На данный момент были созданы на основе базовых схем, следующие схемы:

аналог FileUploadInfo,

аналог IFileUploadInfo,

KmFileUploadService - аналог сервиса FileApiService,

аналог FileUploader аналог.



Так же была заимствована логика подготовки и отправки файла до FileAPI из FileDetailV2 и ConfigurationFileApi.




При выборе некого файла (если помните ранее писал про кнопку Selec file, клик которой создает скрытый input с type = file и реализует клик по нему) после логики схем FileDetailV2 и ConfigurationFileApi файл попадает в FileAPI, где на мой взгляд происходит его подготовка и разбиение на Chunk (далее Чанки).

После всех манипуляций дынный Api посылает его в KmFileUploadService
 , который вызывает унаследованный FileUploader, конкретно метод Upload, где происходит проверка размера файла, а далее ведется проверка связанная с Чанками (!fileUploadInfo.IsChunkedUpload || fileUploadInfo.IsFirstChunk) и в зависимости от проверки запускается метод Save или AppendData.



Как только текущий Чанк попадает в Save, исходя из нашей логики я организовываю запросы на Ftp по созданию файла (ничего такого, чего нет на MSDN).

Далее как я понимаю идёт обратный запрос к FileAPI который в свою очередь отдает нам следующий Чанк, который попадает в AppendData.



Базовая реализация этого метода подразумевает вызов метода DBLobUtilities.AppendBlob.

В нашем случае мы не используем этот метод, так как сохранение файла в базу у нас не велось. Мы используем аналогичный подход как в нашем Save только указываем WebRequestMethods.Ftp.AppendFile ,который должен нам исходя из следующего Чанка дописать ранее созданный экземпляр файла. По завершению этого действия по идее должен быть обратный ответ от FileAPI, который вышлет нам следующий Чанк и так далее пока все Чанки не кончатся, но этого не происходит. См. приложенный скриншот.

Изображение удалено.



Итак самое главное вопросы:

1. Не удалось найти реализацию базового метода DBLobUtilities.AppendBlob, т.е. что он выполняет и каким образом запросы к FileAPI не прерываются. Потому что в нашем случае весь процесс состоит из двух Чанков, + второй Чанк сильно переписывает и ломает тело файла.

2. Хотелось бы узнать существует ли возможность в наш сервис передать кастомное значение типа пункта назначения фолдера, куда сохранять файл. Есть подозрения что нужно будет расширить FileUploadInfo и интерфейс который он использует, и обязательно расширить UI методы который формируют объект для FileAPI.

 

P.s. если не использовать данный подход, а работать с обыкновенным FileReader и читать тело файла readAsDataURL то всё бы ничего, но вот если файл весит свыше 10мб у сервиса начинается батхерт со временем обработки запроса.

Нравится

2 комментария

Не понимаю зачем зарывать в такие дебри. У меня сработало так:

1) Создал миксин, обрабатывающий  клик и загрузку.

define("TmUploadMixin", ["MaskHelper", "ConfigurationFileApi"],
	function(MaskHelper) {
 
		Ext.define("Terrasoft.configuration.mixins.TmUploadMixin", {
		alternateClassName: "Terrasoft.TmUploadMixin",
 
		/**FILE_IMPORTER**/
		onFilesSelected: function(file, tag) {
			if (file.length <= 0) {
				return;
			}
			var config = this.getUploadConfig(file, tag);
			this.upload(config);
		},
		getUploadConfig: function(file, tag) {
			return {
				uploadWebServicePath: "TmFilesUploader/Upload", // "FileApiService/Upload"
				scope: this,
				columnName: "columnName",
				parentColumnName: "parentColumnName",
				parentColumnValue: "parentColumnValue",
				onFileComplete: this.onFileComplete,
				entitySchemaName: tag,
				files: file,
				isChunkedUpload: false
			};
		},
		upload: function(config) {
			MaskHelper.ShowBodyMask();
			Terrasoft.ConfigurationFileApi.upload(config);
		},
		onFileComplete: function(error, xhr, file, options) {
			MaskHelper.HideBodyMask();
			//debugger;
			var data = JSON.parse(Terrasoft.decode(xhr.responseText));
			if (data.error !== "False") {
				this.showInformationDialog("Ошибка импорта. Подробнее — в консоли");
				console.log(data.data);
			} else {
				this.showInformationDialog("Записей импортировано: "+data.data);
				//this.reloadGridData();
			}
		}
 
	});
	return Terrasoft.TmUploadMixin;
});

 

2) Создал сервис

namespace Terrasoft.Configuration.TmFilesUploader
{
	using System;
	using System.IO;
	using System.Net;
	using System.Data;
	using System.Runtime;
	using System.Collections.Generic;
	using System.Runtime.Serialization;
	using System.ServiceModel;
	using System.ServiceModel.Web;
	using System.ServiceModel.Activation;
	using System.Web;
	using System.Web.SessionState;
	using Terrasoft.Common;
	using Terrasoft.Core;
	using Terrasoft.Core.Factories;
	using Terrasoft.Core.Entities;
	using Terrasoft.Configuration.FileUpload;
	using Terrasoft.Web.Common;
 
 
	[ServiceContract]
	[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
	public class TmFilesUploader : BaseService, IReadOnlySessionState
	{
		[Obsolete]
		public const string HeaderContentRange = "Content-Range";
		[Obsolete]
		public const string HeaderRange = "Range";
		[Obsolete]
		public const string HeaderContentDisposition = "Content-Disposition";
 
		[OperationContract]
		[WebInvoke(Method = "POST", UriTemplate = "Upload", ResponseFormat = WebMessageFormat.Json)]
		public string Upload(Stream fileContent) {
			string value;
			bool error=false;
			try {
				IFileUploadInfo fileUploadInfo = new FileUploadInfo(fileContent, new HttpRequestWrapper(HttpContext.Current.Request);
				/**стрим будет валяться в fileUploadInfo.Content**/
			} catch (Exception ex) {
				value = ex.Message+"|"+ex.Source+"|"+ex.StackTrace+"|"+ex.InnerException;
				error = true;
			}
 
			return "{\"error\":\"" + error + "\",\"data\":\"" + value + "\"}";
		}
	}
}

И как бы всё... Ну единственно отрубил chunkedUpload и оставил дефолтный террасофтовский метод по проверке веса файла (думаю можно отредактировать если надо)

Варфоломеев Данила пишет:

Варфоломеев Данила

Добрый день/ночь, вот что получаем в итоге при ~20+ мб весом файла, ограничение Террасофта в настройке повысил. Понимаю что файл надо разбить каким-то образом на осмысленные части и грубо говоря из сервиса append'ить эти части между собой. Но мысли пока зашли в тупик.

Есть предположения у Вас коллега? 

Показать все комментарии

Добрый день!
Возвращаясь к filestream (топик https://community.terrasoft.ru/forum/topic/5410), можно его все-таки использовать в Террасофте для таблицы файлов или нет?
TS 3.4.1.
SQL 2014
Террасофт умеет работать с данными типа varbinary(max) filestream?

1) включили возможность файстрима на сервере
2)создали файловую группу для базы
3)в качестве проверки функциональности создали новую таблицу через sql, там поле varbinary(max) filestream-засунули туда данные через sql - ну тут все ок
4) добавили в таблицу tbl_Files новое поле FileData1 varbinary(max) filestream; (перед этим пришлось удалить триггер к этой таблице - на instead of delete)
5) Пошли в террасофт администратор, там взяли в таблице tbl_Files, полe FileData сменила на FileData1 (не меняя при этом тип, и не сохраняя в бд потом таблицу (потому как не видим типа varbinary в приложении), пошли в sq_Files - там выбрали FileData1 вместо FileData.
6) Зашли в террасофт, добавили файл, файл даже добавился файловую группу, но террасофт его не может потом прочесть, потому что видимо не подозревает про тип варбинари с признаком файлстрим

Есть пути решения?

Нравится

1 комментарий

Работа с filestream обсуждалась тут и тут. Также тут обсуждают альтернативные реализации с хранением в другой базе.

Показать все комментарии

Добрый день!

Как в TS 3.X лучше работать с zip-архивами (распаковать файлы, упаковать файлы)?

Нравится

2 комментария

Думаю, стоит использовать какой-то COM-объеккт и вызывать его методы. Например, стандартный в Windows объект Shell.Application тоже умеет работать с ZIP.

Добрый день.
Все зависит от поставленной задачи.

Самый простой способ, это использовать встроенный Zip-архиватор ОС Windows.

Пример использования:

function Main() {
    var zFolder = "c:\\log.zip";
    var eFolder = "c:\\log";
 
    ExtractFileFromZip(zFolder, eFolder);
    CompriseFileToZip(eFolder, zFolder);
}
 
function ExtractFileFromZip(PathFrom, PathTo) {
    var FSO = new ActiveXObject("Scripting.FileSystemObject");
    var App = new ActiveXObject("Shell.Application");
    if (FSO.FolderExists(PathTo)) {
    	FSO.DeleteFolder(PathTo, true);
    }
    FSO.CreateFolder(PathTo);
    var FolderPathTo = FSO.GetFolder(PathTo).Path;
    var FolderPathFrom = App.Namespace(FSO.GetFile(PathFrom).Path);
    var ZipItems = App.Namespace(FolderPathFrom).Items();
    App.Namespace(FolderPathTo).CopyHere(ZipItems);
}
 
function CompriseFileToZip(PathFrom, PathTo) {
    var Str = "PK%05%06%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00";
    var ForWriting = 2;
    var FSO = new ActiveXObject("Scripting.FileSystemObject");
    var App = new ActiveXObject("Shell.Application");
    if (FSO.FileExists(PathTo)) {
    	FSO.DeleteFile(PathTo, true);
    }
 
    var Itm = FSO.OpenTextFile(PathTo, ForWriting, true);
    Itm.Write(unescape(Str));
    Itm.Close();
 
    var Enm = new Enumerator(FSO.GetFolder(PathFrom).Files);
    for (; !Enm.atEnd(); Enm.moveNext()) {
    	Itm = Enm.item();
    	App.Namespace(FSO.GetFile(PathTo).Path).CopyHere(Itm.Path);
	}
}
Показать все комментарии

Добрый день!
Как можно выгрузить все файлы из детали файлы и ссылки в сетевую папку на диск?
Сделать это нужно программно.

Нравится

6 комментариев

Добрый день, Александр!

bpmonline не предполагает таких операций. Тем не менее данную задачу можно попробовать реализовать через бизнес процесс. Вам необходимо перебрать все записи в соответствующей таблице (объекте). Для каждого раздела объект "Файлы и ссылки" свой (как и таблица в базе данных).

1) Начальный элемент
2) Получение данных о файле элементом "Чтение данных"
3) "Исключающее ИЛИ" с двумя условными потоками (если файлов для выгрузки больше нет, то конец процесса), иначе идем дальше.
4) Элемент "Задание-сценарий" с приблизительным кодом:

   public static string SaveDecompressFile(string FileName, SqlBytes CompressedFile)
    {
        if (CompressedFile.IsNull)
        return "Error";
 
        DeflateStream decompress = new DeflateStream(CompressedFile.Stream, CompressionMode.Decompress, true);
 
        try
        {
 
            FileStream file = File.Create(FileName);
 
            int sourcebyte = decompress.ReadByte();
            while (sourcebyte != -1)
            {
                file.WriteByte((byte)sourcebyte);
                sourcebyte = decompress.ReadByte();
            }
 
            file.Close();
        }
 
        catch (Exception)
        {
            return "Error";
        }
 
        finally
        {
            decompress.Close();
            decompress = null;
        }    
 
        return "OK";
    }

(источник)

Далее возвращаемся на элемент "Чтение данных".

В таком бизнес процессе необходимо будет увеличить максимальное число повторений (настраивается в свойствах процесса).

Все оказалось даже проще.
Может кому-то пригодится
Для работоспособности в using нужно добавить System.IO

//получаем stream из Entity 
MemoryStream ms=documentFile.GetStreamValue("Data");
//записываем его в файл
ms.SaveToFile(dir+name);  

ниже приведен полный код метода, используемого в сервисе

 [OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped,
    RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
public void fileUnloadling(
	string documentId, //Id докумена
	string documentNumber
	)
{
	if (System.Diagnostics.Debugger.IsAttached)
	{
	    System.Diagnostics.Debugger.Break();
	}
	var UserConnection = (UserConnection)HttpContext.Current.Session["UserConnection"];
	var dir=Terrasoft.Core.Configuration.SysSettings.GetValue(UserConnection, "SxFilePathForUnloading")+documentNumber+"\\";
	EntitySchema schema = UserConnection.EntitySchemaManager.GetInstanceByName("DocumentFile");
    EntitySchemaQuery esq = new EntitySchemaQuery(schema);
    esq.AddAllSchemaColumns();
    esq.Filters.Add(esq.CreateFilterWithParameters(FilterComparisonType.Equal, "Document", documentId));
    esq.Filters.Add(esq.CreateFilterWithParameters(FilterComparisonType.Equal, "Type", "529BC2F8-0EE0-DF11-971B-001D60E938C6")); //тип файл
 
    EntityCollection documentFileEntities = esq.GetEntityCollection(UserConnection);
    foreach (Entity documentFile in documentFileEntities  )
    {
        var name=documentFile.GetTypedColumnValue<string>("Name");
		MemoryStream ms=documentFile.GetStreamValue("Data");
        try
		{
			if (!Directory.Exists(dir))
                    {
                        Directory.CreateDirectory(dir);
                    }
			ms.SaveToFile(dir+name);  
		}
		catch (Exception e) 
		{
 
		}
    }
 
}    }

Пащенко Александр Сергеевич

а как мы определяем из детали какого объекта файлы и ссылки и какой именно записи это объекта забираем файлы?

Сафронов Иван Александрович,

В моем случае не нужно было универсальное решение для всех объектов. Как вариант, можете в параметрах прокидывать название объекта или родительской схемы и идентификатор записи. 

Какая у Вас задача?

Пащенко Александр Сергеевич,

После завершения активности хотелось бы выгружать все приложенные файлы на детали Файлы и ссылки в отдельную папку в хранилище.

Сафронов Иван Александрович,

Тогда можете в БП отловить сигналом завершение активности, а дальше скриптом выгружать из ActivityFile.

Учтите, что в детали еще могут быть ссылки. Нужно в запрос добавить фильтр по типу

Схемы файлов обычно называются по шаблону [название раздела] + "File"

Показать все комментарии

bpm 7.2 on-site

Можно ли сделать, чтобы файлы не скачивались, а сразу предлагалось открытие системой?
Или только через ссылки, так можно сделать?

Нравится

2 комментария

Здравствуйте, Илья.

Вам необходимо активировать настройку в используемом Вами браузере:

Google Chrome:

http://i62.fastpic.ru/big/2014/0523/33/6e4b9e46c9a40fe7d09b623db5e6dc33.png

Mozilla Firefox:

http://i62.fastpic.ru/big/2014/0523/33/0ce94959f221827fa4b780f59028e133.png

"Соколов Илья Андреевич" написал:файлы не скачивались, а сразу предлагалось открытие системой

Это абсурд в контексте современных представлений о безопасности браузеров

Показать все комментарии

Имеется деталь "контакты" и у каждого контакта есть, привязанные к нему файлы. Мне необходимо на другой детали получить список привязанных файлов. В дереве нашел dataset ds_Files (при выводе этих данных на экран все работает - выводятся все файлы для всех контактов). Дело остается за малым - отфильтровать по ContactID, однако данное поля в датасете ds_Files нет. Я не могу найти это самое связующее поле. Кто-нибудь знает?

Нравится

2 комментария

Если вдруг кому-нибудь пригодится:

Все файлы хранятся в таблице tbl_Files, информация о принадлежности файла к записям разделов хранится в таблицах развязки (например, tbl_FileInAccount, tbl_FileInContact).

Соответственно, для получения названия файла (колонка Link таблицы tbl_File), Вам необходимо получить идентификатор записи файла из таблицы развязки, отфильтровав записи по идентификатору записи раздела. Затем, получить информацию из таблицы файлов по идентификатору.

Здравствуйте, Александр Сергеевич!

У Вас остались еще вопросы касательно данной темы?

Показать все комментарии