Binary data in 1s. Working with binary data. Retrieving a file from temporary storage

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.
With the help of these objects, it is possible to 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.

  1. The general scheme for working with binary data can be represented as follows:
  2. Stream receiving in progress
  3. A Data Reader or Data Writer object is created.
  4. Using the object created in step 2, the required actions are performed.
  5. The object created in step 2 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 specified upon creation 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 (in English). 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 this example Buffer.ReadInteger32(4); means reading a 32-bit integer starting from byte 4 of the buffer. Thus, if you need to read several 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 that is currently 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. For a detailed description of 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 a Line(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 in various 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
//The Path variable contains 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));

TabDoc.Output(FormElements.TabularDocumentField1);

Eight supports compression of data placed in storage:

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

Procedure RestoreFromStoragePress(Element)

... external processing and reporting:

Procedure LoadProcessingIntoStorage(PropsStorageType)

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

TemporaryFileName = TemporaryFileDirectory()+"report.epf";

BinaryData = PropsStorageType.Get();

BinaryData.Write(TemporaryFileName);

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. The peculiarity of this mechanism is that it is designed for use in a thin client and a Web client and is designed taking into account the restrictions on working with files imposed by Web browsers.

The mechanism is a set of methods that can be used to place data stored locally on the user’s computer in a temporary storage of the information base, transfer this information from the temporary storage to the database, and receive 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 the temporary storage of 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 an empty string, 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 represented not as a path in the file system, but as a variable of 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 user's local file system. 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;

TabDoc.Output(FormElements.TabularDocumentField1);

VariableTemporaryStorageAddress;

If PutFile(TemporaryStorageAddress, SelectedName, True) Then Object.FileName = SelectedName;

TabDoc.Output(FormElements.TabularDocumentField1);

PlaceObjectFile(TemporaryStorageAddress);

endIf;

Procedure RestoreFromStoragePress(Element)

// 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");

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

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.

Binary data in 1C is intended for storing files of arbitrary format. With their help you can:

  • Organize interaction using a binary protocol with various devices;
  • Store files of any formats as metadata object attributes;
  • Convert text data to binary (most often used for sending reports);
  • Work with binary data in memory.

What the system can do

When working with binary data, Platform 8.3 can perform the following actions:

  1. Read and write binary data;
  2. Move data from client to server and back using temporary storage;
  3. Initialize an object of the “Picture” type using binary files;
  4. Read them from the World Wide Web using the objects “Mail Attachment”, “HTTP Connection”, etc.
  5. Use cryptographic tools to encrypt and sign important attachments;
  6. Using the “Data Hashing” object, calculate the hash function.

Saving data to details

For example, let's create a directory in a test configuration.

In fact, using the same directory to store information about nomenclature and binary image data is a little incorrect. With sufficiently large volumes of data and heavy, large files, unwanted downtime and “brakes” in the operation of the system may occur. From the point of view of the system, it would be much more correct to organize a separate “Pictures” directory, a link to which we could set as a props type.


It is important to note that due to the fact that attributes of the “ValueStorage” type containing binary data are not available in managed application mode, they can only be accessed using the FormAttributesValue method.


The message field represents a value store binary data record.

Reading data from props

Let's create a processing that will output the file stored in binary form in our configuration into a spreadsheet document (this is necessary, for example, to print a company logo).


Basically, this is all the code we need. Using the Get() operator, we read the binary data stored in the corresponding directory attribute and transfer it to the “Picture” object, which will be shown in the upper left cell of the form’s spreadsheet document (Fig. 9).

Fig.9

Data Conversion

It is not common, but it happens that when working with non-standard exchanges with external systems, it is necessary to convert data from binary format to Base64 format or vice versa.

In most cases, the platform automatically converts the data; if this does not happen, you need to use global translation functions:

  1. Base64String – converts the specified value into a string of the corresponding encoding;
  2. Base64Value – does the reverse conversion.

Optimization of the above code

The code presented in Fig. 4 certainly works, but with one significant caveat: if the “Modality use mode” checkbox is selected in the configuration properties (Fig. 10). Otherwise, using it will cause an error.
Fig.10

To prevent this from happening, while in the directory element form module, go to the menu Text->Refactoring->Deprecated synchronous calls->Convert module calls.

After some time, synchronous calls will automatically be converted to asynchronous, and the code will take the form (Fig. 11)

Fig.11