How to save binary data to a 1c file. Working with binary data. Reading data from props

Print (Ctrl+P)

16.3. Working with binary data

16.3.1. general information

When implementing application solutions, there may be situations where it is necessary to analyze various binary data. For example, you need to determine the file type using a signature or perform some manipulations with a picture. To work with binary data, 1C:Enterprise provides special software interfaces. Next, we will look at the possibilities for working with binary data.
All work with binary data is based on the concept of a stream. Flow is a logical generalization of an arbitrary (in general case) data source (Stream object). The system does not provide the ability to create an independent Stream object that is not associated with any source. But there are derived objects that can be created - a stream associated with a file on disk (FileStream object) or a stream created in memory (MemoryStream object). A stream allows you to both read data and write it. To determine the possibility of performing certain operations, a stream (and derived objects) has special methods that allow you to determine which
operations are available with this thread (methods AvailableRecord(), AvailableRead(), AvailableChangePosition()).
If you need to work with a stream at a higher level, in particular, read/write data such as a number (of different bit depth) or a string, then the DataRead/DataWrite objects are intended for this. Using these objects, you can take a more structured approach to the binary data located in the stream. So, for example, knowing the format of a file, you can quite comfortably read such a file, obtaining the necessary data from the headers (which, as a rule, are represented by the types number and string), skipping unnecessary data blocks and loading the necessary ones for processing.
The general scheme for working with binary data can be represented as follows:

  1. Stream receiving in progress
  2. A Data Reader or Data Writer object is created.
  3. Using the object created in step 2, the required actions are performed.
  4. The object created in step 2 is closed.
  5. If no more operations are required, the stream obtained in step 1 is closed.
  6. If you need to continue working with the stream, you can set a new position in the stream (if this operation is supported) and continue working starting from step 2.

It is worth noting that it is possible to combine paragraphs 1 and 2. In other words, the system provides the ability to create objects Read Data/Write Data directly from, for example, a BinaryData object.
To perform various operations with binary data, the system provides the ability to obtain some part of the stream as a separate fragment with random (byte-by-byte) access (object BufferBinaryData). The buffer size is set when created and cannot be changed later. When working with a binary data buffer, it is possible to work with numbers of different bit depths as
as one whole. In this case, it is possible to specify the byte order in words: “little endian” or “big endian” (big endian). It is also possible to split one buffer into several and combine several binary data buffers into one resulting buffer.
It is important to note that working with a binary data buffer can significantly simplify the implementation if working with binary data is implemented on the client application side in asynchronous mode. In this case, reading data into the buffer will be performed as an asynchronous operation, and working with the buffer data will be synchronous.
Working with binary data is available on the client side (including the web client) of the application and on the server side, as well as in synchronous and asynchronous work schemes. Further examples will use a synchronous work scheme.

16.3.2. Reading binary data

As an example of reading binary data, we will consider the task of determining the correct file format that was selected in the system for further use. A .wav file with audio data will be used as the file being checked. To store .wav files, Resource Interchange File Format (RIFF) is used, a description of which is given at the link:

https://msdn.microsoft.com/enus/library/windows/desktop/ee415713.aspx (at English language). For the reading example the following format information will be used:
1. The first 4 bytes of the file contain the format identifier: RIFF.
2. the next 4 bytes contain the size of the actual audio data in little-endian byte order.
3. The next 4 bytes contain the text type of data used: WAVE.
To perform these actions you will need the following code in the built-in language:

Read = New ReadData(FileName, ByteEndian.LittleEndian);
FileFormat = Read.ReadCharacters(4);
DataSize = Read.ReadInteger32();
FileType = Read.ReadCharacters(4);
If File Format<>“RIFF” Then
Report("This is not a RIFF file");
Return ;
EndIf ;
If FileType = “WAVE” Then
Report (“This is a WAV file with data, size ” + DataSize + ” bytes”);
Otherwise
Report (“This is not a WAV file”);
Return;
endIf;

Let's look at the example in more detail.
First, the file whose name is contained in the FileName variable is opened, the file is opened for reading ( FileOpenMode.Open), will only read from the file ( FileAccess.Read) and a 16-byte buffer will be used for reading.
Then a stream is generated for reading data, which will have the least significant byte order for data of the Number type. Then 4 characters, a 32-bit integer, and 4 more characters are read from the resulting stream. The resulting data is analyzed and based on the results of the analysis, a decision is made as to whether the selected file is a .wav file or not.

16.3.3. Writing binary data

Writing binary data to a file, in the simplest case, is done as follows:

Entry = New WriteData(FileName);
For Index = 0 To 255 Cycle
Write.WriteByte(Index);
EndCycle;
Record.Close() ;

This example writes to a file a sequence of bytes from 0 to 255 (0xFF in hexadecimal). This is the simplest recording option.
You can also use a method similar to the reading method discussed in the previous example, where a file stream is obtained and data is written to this file stream.

16.3.4. Working with a binary data buffer

As mentioned above, the binary data buffer provides a convenient way to manipulate fragments of binary data.
Not only reading data is supported, but also writing.
As an example, we will consider parsing the RIFF file header from the data reading example (see here). To build the example, exactly the same information about the file format will be used. Thus, it is necessary to read from the source file a buffer the size of the file header. The header consists of three 4-byte fields. Thus, 12 bytes need to be read.

Buffer = New BufferBinaryData(12);
File = FileStreams.Open(Temporary Files Directory() + “Windows Logon.wav”, FileOpenMode.Open, FileAccess.Read);
File.Read(Buffer, 0, 12);
Size = Buffer.ReadInteger32(4);
StreamString = newStreamInMemory(Buffer);
StreamRows.Go(0, PositionInStream.Start);

FileFormat = ReadLines.ReadCharacters(4, “windows-1251”);
ReadLines.Close();
StreamRows.Go(8, PositionInStream.Start);
RowReader = new DataReader(RowStream);
FileType = ReadLines.ReadCharacters( 4, “windows-1251”);
ReadLines.Close();

The process of getting data into a binary data buffer is nothing special. Further operations require some comments. Reading numbers of any supported bit depth is possible from any position in the buffer. IN in this example Buffer.ReadInteger32(4); means reading a 32-bit integer starting from byte 4 of the buffer. Thus, if you need to read multiple numbers located in different places in the buffer, this can be done without direct positioning in that buffer.
Reading a string, however, is not supported by the binary data buffer. Therefore, you should use an object that allows you to do this: Read Data. A DataReader object cannot be created from a binary data buffer. But based on a binary data buffer, you can create a stream that is a universal intermediary between the physical storage location of information (file, binary data buffer) and a high-level object that allows you to work with this data.
When a DataReader object is created based on a stream, it begins reading data from the position in this moment installed in the stream. Therefore, in the example, the position in the stream is first set, and then a DataReader object is created and the required number of characters is read. Detailed description for the difference between the number of bytes and characters when reading strings, see the next section 16.3.5

16.3.5. Features of use

When using binary data, you should take into account the features of working with data of the String type. The peculiarity is that the length of the string that the global context function StrLength() returns is measured in characters. In symbols, you should indicate the size of the data to be read/written in the methods for writing/reading strings in objects for working with binary data ( ReadCharacters(),
ReadString(), WriteCharacters(), WriteString()). However, there is no unambiguous option for converting the length of a string in characters to a similar parameter in bytes. Depending on the contents of the string and the encoding, this ratio will be different. Therefore, when working with any data structures that include strings of variable length, you should clearly understand in what units the string lengths are expressed.
If in the available data the string length is indicated in bytes, and the string is specified in a multi-byte variable-length encoding (for example, UTF-8), then using binary data objects, reading such a structure from a file into data of the String type is generally impossible.
But in this case, you can easily change the read/write position in the file stream. If the length of a string is specified in characters, then it becomes possible to read such a string into data of the String type, but it becomes impossible to change the read/write position in such a stream.
To get the length of a string in bytes, you can use the following function to convert the string to a BinaryData object:

Function Get Binary Data From String(Value StrParameter, Value Encoding = “UTF-8”)
MemoryThread = NewMemoryThread;
Writer = New WriteData(StreamMemory);
Writer.Write String(StrParameter, Encoding);
Writer.Close();
Return StreamMemory.CloseAndGetBinaryData();
EndFunction

The actual size in bytes can be obtained by calling the Size() function on the BinaryData object, which is obtained as a result of the function.
Simultaneous use of objects is not recommended Read Data/Write Data and stream objects. If between two successive reading operations from ReadData or two successive write operations to WriteData there is a change in position in the stream with which the Ch objects work ShadowData/WriteData– an exception is generated. Thus, the following example demonstrates the correct change of position in a stream when writing data to a stream:

Stream = newStreamInMemory();

WriteData.WriteString("Hello World!");
WriteData.Close();
Stream.Go (0, PositionInStream.Start);
DataWrite = newDataWrite(Stream);
WriteData.WriteString("Bye!");
WriteData.Close();
The following example hi to an exception being thrown:

Stream = NewStreamInMemory();

WriteData.WriteString(“Hello, world!”);
Stream.Go(0, PositionInStream.Start);
// The next line will throw an exception
WriteData.WriteString(“Bye!”);
At the same time, situations are possible when the system behavior will be incorrect, but no errors will be generated:

Stream = GetStream();
ReadData = new ReadData(Stream);
TestString = ReadData.Read();
InitialPosition = Stream.CurrentPosition();
DataWrite = newDataWrite(Stream);
WriteData.WriteString(“Unexpected string”);
WriteData.Close();
Stream.Go(InitialPosition, PositionInStream.Start);
// In general, it is impossible to determine what value will be placed in the TestString2 variable
TestLine2 = ReadData.ReadLine();

The behavior described in this section is caused by o Data Reader/Data Writer objects use their own buffers when working with a stream. As a result, the actual position of the thread differs from the logical position, which is formed as a result of the completed operations.
Also, the simultaneous use of Data Reader and Data Writer objects, which use one thread for their work, is not supported.

The 1C:Enterprise 8 technology platform allows you to save arbitrary files in the information base, retrieve them from there and use them different ways. Let's look at these operations using examples.

Before uploading a file to the 1C information base, you need to obtain the full address of the file on disk. Working with file selection dialogs is described in .

To store files, use an attribute (or register resource) with the type StorageValues.

Uploading an arbitrary file to the 1C information base

Any file can be represented as binary data and loaded into Value Storage.

When converting binary data to an object StorageValues design used new StorageValues(Data, Compression) with two parameters:

  1. Data— binary data that needs to be stored in storage
  2. Compression— compression ratio of the Deflation algorithm. Integer in the range -1...9. -1 is the default compression level. 0 - no compression, 9 - maximum compression. Default value: -1. The parameter is optional; if not specified, then compression is not used.

//Convert the file to binary data
File = New BinaryData(Path) ;

//Create a new Value Storage object

DataStorage = NewValueStorage(File, NewDataCompression(9) ) ;

Saving an arbitrary file from the 1C infobase to disk

To save a file from the 1C database to disk, you need to determine the path and file name. To do this, there is a file saving dialog, working with which is described in.

//Get binary data from storage
//Data Storage - attribute of an object with the Value Storage type

//Write the received data to disk
//IN variable Path is the full address of the file on disk
Data. Write(Path) ;

Viewing a file located in the 1C information base

To view a file saved in the database, you must have an application installed on your computer that opens the file.

//Get the name of the temporary file with the required extension
//In the Extension variable you need to put the file extension, for example "pdf"
Path = GetTemporaryFileName(Extension) ;

//Receive data from storage
//Data Storage - attribute of an object with the Value Storage type
Data = Datastore. Get() ;

//Write data to a temporary file
Data. Write(Path) ;

//Trying to open the file in the intended application
//If the application is not found, the system dialog "Open with..." will appear.
LaunchApplication(Path) ;

Almost any information can be stored in a value store, e.g.

... pictures (photos):

CurrentImage.Object = SprFabric.Link; CurrentImage.DataType = Enumerations.Types of Additional Information of Objects.Image; Storage = NewValueStorage(NewPicture, NewDataCompression()); CurrentImage.Storage = Storage.Get();

// in this place it displays everything... Form Elements.PictureField1.Picture = Storage.Get(); CurrentImage.Write();

...spreadsheet document:

TabDoc=New TabularDocument; TabDoc.Output(FormElements.TabularDocumentField1); Storage=NewValueStorage(TabDoc); Write();

End of Procedure

Procedure RestoreFromStoragePress(Element)

TabDoc=Storage.Get(); If TabDoc<>Undefined ThenFormElements.TabularDocumentField1.Output(TabDoc); endIf;

End of Procedure

...arbitrary files (binary data):

XZ = NewValueStorage(NewBinaryData(file));

Eight supports compression of data placed in storage:

XZ = NewValueStorage(NewBinaryData(file),NewDataCompression(9));

... external processing and reporting:

Procedure LoadProcessingIntoStorage(PropsStorageType)

CompressionRate = NewDataCompression(9); //9 maximum PropsStorageType = New StorageValues(New BinaryData("c:\reports\report.epf", Compression Rate));

End of Procedure

Procedure StartProcessingFromStorage(PropsStorageType)

TemporaryFileName = TemporaryFileDirectory()+"report.epf"; BinaryData = PropsStorageType.Get(); BinaryData.Write(TemporaryFileName); ExternalProcessing = ExternalProcessing.Create(TemporaryFileName); ExternalProcessing.GetForm().Open();

End of Procedure

Working with storage

If it was Binary Data, then it can be restored from the value store using the Get method and written to a file using the Write() method.

If TypeValue(Storage)<>Type("BinaryData") Then

BinaryData = Storage.Get();

BinaryData = Storage;

endIf; BinaryData.Write(FileName);

If it was, for example, a Word document (doc file, or other registered file type), then it can be opened like this:

LaunchApplication(FileName);

To clear a field of type Value Storage, you need to assign it Undefined:

PropsStorage = Undefined;

Working with files and pictures in the built-in language 1C:Enterprise 8

Purpose

The managed application implements a new mechanism for working with files. It provides file exchange between the infobase and the client application. Feature this mechanism is that it is intended for use in thin client and Web Client and is designed to take into account the file handling restrictions imposed by web browsers.

The mechanism is a set of methods that can be used to place data stored locally by the user into temporary storage information base, transfer this information from temporary storage to a database and get it back to the user's computer. The most common application problems solved by this mechanism are the storage of accompanying information, for example, images of goods, documents related to contracts, etc.

Method Scope

Temporary storage

Temporary storage is a specialized area of ​​the information base in which binary data can be placed. The main purpose is to temporarily store information during client-server interaction before it is transferred to the database.

The need for temporary storage arises because the web browser operating model requires that the user-selected file be transferred directly to the server without the possibility of storing it on the client. When a file is transferred, it is placed in temporary storage and can then be used when writing an object to the database.

The most typical application task solved by temporary storage is providing access to files or pictures before the object is recorded in the information base, for example, in the form of an element.

A file or binary data placed in storage is identified by a unique address, which can later be used in write, read, or delete operations. This address is given by methods for writing a file to temporary storage. A separate method in the built-in language allows you to determine whether the passed address is an address pointing to data in temporary storage.

Information base

The mechanism allows you to access binary data stored in attributes of the Value Storage type.

As in the case of temporary storage, access to information is possible through a special address. You can get it through a special method by passing a link to an object or an information register entry key, and the name of the attribute. In the case of a tabular part, it is additionally required to transfer the row index of the tabular part.

Methods for working with files have limitations when working with infobase details. For them, unlike temporary storage, only reading information is available, but not writing or deleting it.

Description of methods for working with files

Saving data to temporary storage

The most typical scenario for using this mechanism involves initially placing user data in temporary storage. There are two methods for this: PlaceFile() and PlaceFileInTemporaryStorage().

The first method, PlaceFile(), places a file from the local file system into temporary storage. The method can accept a target address in storage. If it is not defined or is empty line, then a new file will be created and the method will return its address through the corresponding parameter.

If the parameter that determines the interactive mode of operation is True, then the method will display a standard file selection dialog box in which you can select a file to place in storage. In this case, the method will also return the address of the selected file.

As a result, the method returns False if the user interactively refused to perform an operation in the file selection dialog. The method is only available on the client.

The second method, PlaceFileInTemporaryStorage(), is similar to the previous one, except that it is available on the server, and the data to be written to temporary storage is not represented as a path in the file system, but as variable type BinaryData. Likewise, if no target address is specified, a new file is created in the storage. Its address is returned as the result of the function.

Retrieving a file from temporary storage

When writing an object to the infobase, you may need to extract data from temporary storage and place it, for example, in an attribute. There is a corresponding server method for this - GetFileFromTemporaryStorage(). This method retrieves data from temporary storage and returns it as a result. To do this, you need to specify the address in temporary storage. This address is returned by the above-described methods PlaceFile() and PlaceFileInTemporaryStorage() if they are executed successfully.

Deleting a file from temporary storage

After the data is saved in the details, the file in temporary storage can be deleted. For this purpose, there is a method DeleteFileFromTemporaryStorage(), which deletes a file from temporary storage. The method takes as a parameter the address of a file in temporary storage. Available on the server.

Checking the address for temporary storage

The file address can indicate both temporary storage and details in the infobase. To check its type, there is a method This isTemporaryStorageAddress().

It checks that the passed address is an address pointing to the store. Returns True if the address points to temporary storage. The method is available on the server.

Receiving the props address

After the data is placed in the details in the infobase, you may need to access it using file methods.

But before you receive data, for example from a property, you need to get the address of this property. For this purpose, there is a method GetFileAddressInInformationBase().

Its purpose is to return the file address in the infobase according to the original parameters. To do this, you need to pass the object key (this can be either a link to the object or an information register entry key) and the name of the attribute. If you need to get the address of a file stored in a tabular part attribute, before the attribute name in the parameter specifying the attribute name, you need to add the name of the tabular part and a dot “.”. The method is available on both the client and the server.

Retrieving a file from the infobase

The GetFile() method receives a file from the infobase and saves it to the local file system user. The first parameter specifies the address of the file in the props or temporary file storage. The second parameter specifies the destination location of the resulting file. In non-interactive mode, you must specify the path. In interactive mode, the parameter is optional.

By default, the method is executed in interactive mode, that is, the last parameter is True. This means that a dialog box is displayed in which you can specify an action with the received file: run it or save it to a user-specified location. If interactive mode is active and the Target disk file path parameter is not specified, the file open operation is not available. Returns a boolean value. False means the user chose to cancel the operation in the interactive save file dialog box.

Example of using file methods

// Receiving a file from disk in interactive mode // and placing it in temporary storage &On the Client Procedure SelectDiskFileAndWrite()

Variable SelectedName; VariableTemporaryStorageAddress; If PutFile(TemporaryStorageAddress, SelectedName, True) Then Object.FileName = SelectedName; PlaceObjectFile(TemporaryStorageAddress); endIf;

End of Procedure

// Copying a file from temporary storage to a directory // attribute, recording an object, deleting a file from temporary // storage &On the Server Procedure Place Object File (Temporary Storage Address)

Directory Element = Form AttributesValue("Object"); BinaryData = GetFileFromTemporaryStorage(TemporaryStorageAddress); Directory Element.File Data = NewValueStorage(BinaryData); FilePathOnDisk = New File(DirectoryItem.FileName); Directory Item.FileName = FilePathOnDisk.Name; Directory element.Write(); Modified = False; DeleteFileFromTemporaryStorage(TemporaryStorageAddress); ValueВFormAttributes(Directory Element, "Object");

End of Procedure

// Reading the file from the props and saving it // to local disk in interactive mode &On the Client Procedure ReadFileAndSaveToDisk()

Address = GetFileAddressInInformationBase(Object.Link, "FileData"); GetFile(Address, Object.FileName, True);

End of Procedure

Support for addresses in the picture field

The Picture Field control supports displaying a picture specified by the address of a file in temporary storage or in a database.

To do this, you must set the attribute in the Data property of the form element string type. The value of this attribute will be interpreted as the address of the picture.

Example // Binding the image field to the image address in temporary // storage. AddressPictures form details of string type

PlaceFile(PictureAddress,True)

Picture.Data = AddressPictures

Limitations when working with the Web client

The operation of the described mechanism when using the Web client has some limitations. These restrictions are related to the browser's security model. So, for example, the client cannot independently save a file to the local file system, that is, only the interactive version of the client methods PlaceFile() and GetFile() is available. An exception is thrown when attempting to use non-interactive mode. Dialog boxes, displayed interactively, are specific to a particular browser type.

Features when working with Value Storage on the Client

Problem:

When a Document has an attribute of the Value Storage type in the tabular section, it slows down the opening of the document form if this attribute contains large data.

Supposed reason:

Perhaps, when opening a form, it is not the link to the data located in the Value Store that is sent to the client, but the data itself.

Solution

  • In the properties of the form's table attribute there is a flag "Always use". If it is set, the contents of the field are always transferred between the server and the client - for example, when opening a form. This flag must be disabled, but this must be taken into account in the code, since by default there will be no value for this field on the client. An example can be found in 1C:Archive.

It's even better to use temporary storage to transfer files between client and server.