Импорт картики richtextitem в excel, используя dxl

Lia

New member
22.04.2015
4
0
#1
Задача следующая:
1)достать из RTI картинку в формате base64;
2)потом её сохранить в формате jpg;
3)вставить в нужную ячейку Excel.
Вот что у меня пока что выходит:
Код:
Dim s As NotesSession
Dim db As NotesDatabase
Dim inputstream As NotesStream
Dim exporter As NotesDXLExporter
Dim domparser As NotesDOMParser
Dim rootElement As NotesDOMElementNode
Dim docNode As NotesDOMDocumentNode
Dim docList As NotesDOMNodeList
Dim itemList As NotesDOMNodeList	'list of <item> nodes
Dim node As NotesDOMNode			'an <item> node
Dim aNode As NotesDOMAttributeNode  'a "name" attribute
Dim i As Integer
Dim pictureBase64 As String
 
Dim filename As String
fileName = "c:\InputFile.dxl"
 
Set db = curdoc.Parentdatabase
Set s = db.Parent
Set inputstream = s.Createstream
 
If inputstream.Open(fileName) Then
Call inputstream.Truncate()
Set exporter = s.Createdxlexporter
exporter.OutputDOCTYPE = False
exporter.RichTextOption = RICHTEXTOPTION_RAW
Call exporter.SetInput(doc)
inputstream.Writetext(exporter.Export(doc))
Set domparser = s.CreateDOMParser
Call domparser.setinput(exporter)
Call exporter.Process 'стало проскакивать
Set docNode = domparser.Document
Set rootElement = domParser.Document.DocumentElement
Set doclist = rootElement.Getelementsbytagname("*")
If docList.NumberOfEntries = 0 Then 
MessageBox "No <document> element nodes in file", , "Error"
Exit Sub
End If
Set itemlist = docnode.Getelementsbytagname("item")
If itemList.NumberOfEntries = 0 Then 
MessageBox "No <item> element nodes in file", , "Error"
Exit Sub
End If
For i = 1 To itemList.NumberOfEntries
Set node = itemList.GetItem(i)
Set aNode = node.Attributes.Getitem(1)
If anode.AttributeValue = "Signature" Then
pictureBase64 = node.Lastchild.Lastchild.Nodevalue
MessageBox  pictureBase64  
End If
Next 
End If
 
'Dim objXML As Variant
'Dim objDocElem As Variant
'Dim writebytes As Variant
'Dim objStream As Variant
 
'Set objXML = CreateObject("MSXml2.DOMDocument")
'Set objDocElem = objXML.createElement("tmp")
'objDocElem.DataType = "bin.base64"
'objDocElem.text = pictureBase64
'writeBytes = objDocElem.NodeTypedValue
'Set objStream = CreateObject("ADODB.Stream")
'objStream.Type = 1 ' TypeBinary
'objStream.Open
'objStream.Write writeBytes 'write binary data to a binary Stream object.
'objStream.SaveToFile "C:\Temp\test.jpg", 2
Суть проблемы, что почему-то стало выкидывать на этапе Call exporter.Process . Почему?
А до этого я получала pictureBase64 картинки и пыталась преобразовать. Но его нельзя было открыть/прочитать. Потом я взяла отдельно преобразовала картинку в форма base64 и там совершенно другой набор символов :( Что я упускаю?
 

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 583
269
#2
inputstream.Writetext(exporter.Export(doc))
Set domparser = s.CreateDOMParser
Call domparser.setinput(exporter)
Call exporter.Process 'стало проскакивать
это вот зачем? есть pipelining, есть postdomprocess (для DOM), есть serialize (тоже для DOM)
[DOUBLEPOST=1430143135,1430142877][/DOUBLEPOST]@rinsk в ртфм лОжил свой пример
Для просмотра контента необходимо: Войти или зарегистрироваться

"классику знать надо" :)
[DOUBLEPOST=1430143405][/DOUBLEPOST]надеюсь @rinsk простит, перепощу код (ссылку же дал)
Код:
Class AttPictureSetClass
	''''''''''''
	s As NotesSession
	db As NotesDatabase
	doc As NotesDocument
	''''''''''''
	attExt As String
	attExtList List As Variant
	'''''''''''
	Public dxp As NotesDXLExporter
	Public par As NotesDOMParser
	Public dip As NotesDXLImporter
	'''''''''''
	Sub New
		Set s=New NotesSession
		Set db=s.CurrentDatabase
	End Sub
	Function ProcessObject (obj As Variant, dbexport As NotesDatabase)
		Set Me.dxp = Me.s.CreateDXLExporter(obj)
		Me.dxp.ConvertNotesbitmapsToGIF=True
		Set Me.par = Me.s.CreateDOMParser(Me.dxp)
		On Event PostDOMParse From par Call ProcessDOM
		Set Me.dip = Me.s.CreateDXLImporter(Me.par, dbexport)
		Me.dip.DocumentImportOption=DXLIMPORTOPTION_UPDATE_ELSE_IGNORE
		Me.dip.DesignImportOption=1
		Me.dip.ReplaceDBProperties=False
		Me.dip.ReplicaRequiredForReplaceOrUpdate=False
		Call Me.dxp.Process
		ProcessObject=Me.dxp.Log
	End Function
	Private Sub ProcessDom (Source As NotesDOMParser)
		Dim rtNode As NotesDOMNodeList
		Dim AtRefList As NotesDOMElementNode
		Dim attExt As String
		Dim attList List As Variant
		Set rtNode=Source.Document.GetElementsByTagName("attachmentref")
		For x=1 To rtNode.NumberOfEntries
			Set AtRefList=rtNode.GetItem(x)
			attExt=LCase(StrRightBack(atRefList.GetAttribute("displayname"),"."))
			If IsElement(Me.attExtList(attExt)) Then
				Call atRefList.SetAttribute("caption",atRefList.GetAttribute("displayname"))
				AtRefList.FirstChild.FirstChild.FirstChild.NodeValue=Me.attExtList(attExt)
			End If
		Next
		Source.Serialize
	End Sub
	Public Sub GetImageRes
		Dim nc As NotesNoteCollection
		Dim GifNodeList As NotesDOMNodeList
		Dim GifNode As NotesDOMNode
		Dim ParentObj As NotesDOMElementNode
		Set nc=s.CurrentDatabase.CreateNoteCollection(False)
		nc.SelectImageResources=True
		nc.SelectionFormula=|@Matches($TITLE;"_att_.???")|
		Call nc.BuildCollection
		If nc.count>0 Then
			Set par = s.CreateDOMParser()
			Set dxp = s.CreateDXLExporter(nc,par)
			dxp.ConvertNotesbitmapsToGIF=True
			Call dxp.Process
			Set GifNodeList=par.Document.GetElementsByTagName("gif")
			For k=1 To GifNodeList.NumberOfEntries
				Set GifNode=GifNodeList.GetItem(k)
				Set ParentObj=GifNode.ParentNode
				Me.AttExtList(LCase(StrRightBack(ParentObj.GetAttribute("alias"),".")))=GifNode.FirstChild.NodeValue
			Next
		End If
	End Sub
	Sub Delete
		If Not Me.db Is Nothing Then Delete Me.db
		If Not Me.s Is Nothing Then Delete Me.s
	End Sub
End Class
Sub Initialize
	Dim s As New NotesSession
	Dim db As NotesDatabase
	Dim doc As NotesDocument
	Dim cl As NotesDocumentCollection
	'''''''''''''''
	Dim attObj As AttPictureSetClass
	'''''''''''''''
	Set db=s.CurrentDatabase
	Set doc=db.UnprocessedDocuments.GetFirstDocument
	Set attObj=New AttPictureSetClass
	Call attObj.GetImageRes
	attObj.ProcessObject doc,db
End Sub
 
Последнее редактирование модератором:

Lia

New member
22.04.2015
4
0
#4
Спасибо, буду разбираться.
а по второму вопросу какие-либо соображения имеются?
Ведь написав:
exporter.RichTextOption = RICHTEXTOPTION_RAW
в rawitemdata содержиться картинка в формате base64 ,
а если я получаю base64 картинки данным скриптом
Код:
Set objStream = CreateObject("ADODB.Stream")
objStream.Type = 1 ' TypeBinary
objStream.Open()
objStream.LoadFromFile("C:\Temp\test.jpg")
readBytes = objStream.Read()
Set objXML = CreateObject("MSXml2.DOMDocument")
objXML.loadXML "<Base64Data />"
Set objDocElem = objXML.documentElement
objDocElem.dataType = "bin.base64"
objDocElem.nodeTypedValue = readBytes
textBase64 = objDocElem.text
,то получаю другой набор символов.

Тое сть набор символов не совпадает

И если я преобразовываю в jpg base64 из rawitemdata, то
Код:
'Dim objXML As Variant
'Dim objDocElem As Variant
'Dim writebytes As Variant
'Dim objStream As Variant
 
'Set objXML = CreateObject("MSXml2.DOMDocument")
'Set objDocElem = objXML.createElement("tmp")
'objDocElem.DataType = "bin.base64"
'objDocElem.text = pictureBase64
'writeBytes = objDocElem.NodeTypedValue
'Set objStream = CreateObject("ADODB.Stream")
'objStream.Type = 1 ' TypeBinary
'objStream.Open
'objStream.Write writeBytes 'write binary data to a binary Stream object.
'objStream.SaveToFile "C:\Temp\test.jpg", 2
получаю ошибку
this not vlid bitmap file, or it format not currently supported.

а если используя textBase64 , то та же картика отображается.
 

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 583
269
#5
в rawitemdata содержиться картинка в формате base64 ,
а если я получаю base64 картинки данным скриптом
не используете КОМ чуть менее чем везде ;)
в домине достаточно бриджей для работы с base64 без COM
второе - я не совсем понимаю, вернее - не понимаю, зачем использовать RAW?
картинки итак хранятся в base64 в DXL, как пример картинка в РТ безо всяких RAW, будет в тегах - <picture width='2048px' height='1536px'><jpeg>Здесь base64</jpeg></picture>
[DOUBLEPOST=1430223423,1430223163][/DOUBLEPOST]как дернуть картинку - ну например с пом. XSLT
[DOUBLEPOST=1430223653][/DOUBLEPOST]еще (фигтекогда выкладывал)
Для просмотра контента необходимо: Войти или зарегистрироваться

если без внешних либ - LAX
 

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 583
269
#6
вот тестовая база - положить (скопировать и буфера) любой док, запустить SAX test агент - получить файло в String rootPath="c:\\" + fsep + "files" + fsep;
 

Вложения

Последнее редактирование модератором:

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 583
269
#7
может вывалить эксепшн - надо экспортеру запретить использовать DTD
создать диру c:\files - оно туда хочет сохранять
 

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 583
269
#8
может вывалить эксепшн - надо экспортеру запретить использовать DTD
вернее так - в тек класспаз должен лежать domino.dtd
в ранних версиях домины (до 7-ой) оно само находило диру xmlschemas (где все и лежит)
но в 9-ке оно мажет ;)
 

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 583
269
#9
патч - там приложен код exporter.setDoctypeSYSTEM(binPath.toString()+File.separatorChar + "xmlschemas"+File.separatorChar+"domino_6_5_4.dtd");
кот. надо раскоментить, для задачи непринципиально, а 6.5.4 присут. в это месте
 

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 583
269
#10
короткий резьюм по БД - агент исполняется над выделенными доками, результат складывает в ФС
результат - все что есть приложенного или встроенного в док, имена задаются по значение миллисекунд + экстеншн
для приложенных файлов - имя как в приложении
используется SAX (с рефлексией - LAX, для кастомного расширения типа вложенных объектов) чтобы не жрать память
[DOUBLEPOST=1430299784,1430299714][/DOUBLEPOST]для большей экономии памяти - переделать на экспорт в файл (в коде - стринг)
 
Симпатии: Понравилось alexas1

Lia

New member
22.04.2015
4
0
#11
вроде всё заработало!)

3)вставить в нужную ячейку Excel.

Код:
xlFilename = "c:\СБД\XLS\" + curdoc.DocName(0) + ".xls"
Set Excel = createObject("Excel.Application")
Excel.Visible = true
Excel.Workbooks.Open(xlFilename)
Set xlWorkbook = Excel.ActiveWorkbook
Set xlSheet = xlWorkbook.ActiveSheet
Set xlCells = xlSheet.Cells
row = 27
Excel.Rows(row).Select
Excel.Selection.ClearContents
xlCells(row,1).Value = curdoc.FromPost(0)
xlCells(row,5).Value = curdoc.FromName(0)
'вставка картинки
Dim r As Variant
Set r = xlSheet.Range("C25")'положение левого верхнего угла картинки
xlSheet.Shapes.AddPicture "C:\Temp\test.jpg", False, True, r.Left, r.Top, 80, 80
Excel.Quit
но теперь понадобилось вставлять не в 27 строку, а в определённую. Решила, что поставлю метку, и найду адрес строки с примечанием.
Код:
Set r = xlSheet.Cells.Find("sign") 
If r Is Nothing Then
MessageBox "no"
End If
r заполняется [Object], но не могу достучаться до адреса ячейки с меткой. Про проверки на Nothing return True
Поэтому когда пытаюсь получить address or row , ошибка Object variable not set.
 

oshmianski

Достойный программист
Lotus team
25.04.2012
555
8
#12
Этак вы до Excel XML Spreadsheet + XSLT скоро дотянете.
Бросьте Вы этот COM, не тратьте на него время.
 

Lia

New member
22.04.2015
4
0
#13
а на что тратить?

каким путём дальше идти?

п.с. Сильно не ругайте, я только месяц как месяц познакомилась с лотусом)
 

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 583
269
#14
а на что тратить?
каким путём дальше идти?
ответ выше
Этак вы до Excel XML Spreadsheet + XSLT
еще есть POI
я только месяц как месяц познакомилась с лотусом)
это меньше всего влияет на решаемую задачу, при условии понимания - что есть ХМЛ и знания, например java
 

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 583
269
#15
Java:
import lotus.domino.*;
import java.io.*;
 
public class JavaAgent extends AgentBase {
	Thread thread1;
	Thread thread2;
 
	public void NotesMain() {
 
		try {
			Session session = getSession();
			AgentContext agentContext = session.getAgentContext();
 
			// (Your code goes here)
			Document doc = agentContext.getDocumentContext();
 
			StringBuffer binPath = new StringBuffer(session
					.getEnvironmentString("NotesProgram", true));
			if (binPath.length() == 0) {
				String sttmp = session.getEnvironmentString("Directory", true);
				binPath.append(session.getEnvironmentString("Directory", true));
				if (binPath.charAt(binPath.length() - 1) == File.separatorChar)
					binPath.setLength(binPath.length() - 1);
				binPath.setLength(binPath.toString().lastIndexOf(
						File.separatorChar));
			}
			System.out.println("Program Path:" + binPath.toString());
			PipedInputStream in = new PipedInputStream();
			final PipedOutputStream out = new PipedOutputStream(in);
			thread1 = new NotesThread(new DataSource(doc, out));// new
																// FileOutputStream("./out.dxl")));
			thread1.start();
			// thread1.join();
			thread2 = new NotesThread(new DataConsumer(in));// new
															// FileInputStream("./out.dxl")));
			thread2.start();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				thread1.join();
				thread2.join();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}
Java:
import java.io.BufferedWriter;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
 
import lotus.domino.*;
 
public class DataSource implements Runnable {
	private Document doc = null;
	private OutputStream outs = null;
 
	public DataSource(Document xdoc, OutputStream stream) {
		outs = stream;
		doc = xdoc;
	}
 
	@Override
	public void run() {
		try {
			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outs,
					"utf-8"));
			System.out.println("*" +this.getClass().getName()+" ->doc's generating...");
			doc.generateXML(bw);
			bw.flush();
			outs.flush();
			outs.close();
			System.out.println("*" +this.getClass().getName()+" ->doc's generated");
			outs = null;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("quit DataSource");
			if (outs != null) {
				try {
					outs.flush();
					outs.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
}
Java:
import java.io.File;
import java.io.InputStream;
 
import lotus.notes.NotesThread;
 
public class DataConsumer implements Runnable {
	private InputStream inputStream = null;
 
	public DataConsumer(InputStream inputStream) {
		this.inputStream = inputStream;
	}
 
	public void run() {
		Lax lax = new Lax();
		// Создаем инстансы классов, кот. обрабатывают конкретные элементы XML
		FileData fdata = new FileData("."
				+ (new Character(File.separatorChar)).toString());
		GifData gifData = new GifData("."
				+ (new Character(File.separatorChar)).toString());
		JpegData jpegData = new JpegData("."
				+ (new Character(File.separatorChar)).toString());
		// Подключаем их к автоматическому хендлеру
		// в классе д.б. методы textOf<элемент> (для обработки содержимого) и
		// start<элемент>, end<элемент> - для условий начала и окончания
		// элемента
		lax.addHandler(jpegData);
		lax.addHandler(gifData);
		lax.addHandler(fdata);
		System.out.println("procesing...");
		lax.parseDocument(true, lax, inputStream);
	}
}
 
Последнее редактирование модератором:

lmike

нет, пердело совершенство
Lotus team
27.08.2008
6 583
269
#16
закоменчен вариант с промежуточным файлом
в 9.0.1 (да и в 8.х полагаю) путь ./ означает IBM\Notes\framework
в системную консоль (Java Debug Console) выводится полный путь (сделал только для файла)
Java:
import org.xml.sax.*;
import java.io.*;
import org.base64.util.*;
 
public class FileData {
	// private String fileName="";
	private FileOutputStream outFile = null;
	private StringBuffer sBuff = new StringBuffer("");
	private String rootPath = "";
	private String filePath = "";
	private File fobj = null;
 
	public FileData(String path) {
		super();
		rootPath = path;
	}
 
	public void textOffiledata(String fileData) {
		// System.out.print(fileData);
		sBuff.append(fileData);
	}
 
	public void startfile(AttributeList list) {
		// за название берем системное время в мс
		filePath = rootPath + list.getValue("name");
		fobj = new File(filePath);
	}
 
	public void startfiledata(AttributeList list) {
		System.out.print("start file->");
		openStream(fobj);
	}
 
	public void endfiledata() {
		closeStream();
		System.out.println("<-end file");
	}
 
	// *************************************************
	private void openStream(File f) {
		try {
			outFile = new FileOutputStream(f);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
 
	private void closeStream() {
		try {
			if (outFile != null) {
				outFile.write(Base64.decode(sBuff.toString()));
				outFile.close();
				System.out.println(fobj.getAbsolutePath());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}