Accessing file system programmatically
Scope: managed applications, mobile applications, and ordinary applications.
1. When 1C:Enterprise tries to access files and directories programmatically, the access is limited by the rights granted to the operating system user.
1.1. When 1C:Enterprise tries to assess a file on the client side, it accesses the file system under the user who started the application. In the general case, this is the current operating system user.
1.2. When 1C:Enterprise tries to assess a file on the server side, the following access rules are applied:
- For client/server infobases, the access is limited by the rights of the user who started the server session
.
- For file-based infobases stored on the web servers, the access is limited by the rights of the user who started the web server.
* Some processes might be started under other users. For more information, see 3.28. swpuser.ini in Appendix 3 to 1C:Enterprise Administrator Guide.
Scope: managed applications, ordinary applications. 2. It is not allowed to save files to the directory returned by the BinDir method. You can use BinDir only to read or execute files. For example, to start 1C:Enterprise in the thin-client mode on Windows, run the following code: RunApp(BinDir() + "1cv8s.exe"); |
3. Even if a configuration is not supposed to be localized to other languages, you must ensure that the programmatically created files support migration to other operating systems and other encodings. To comply with this requirement, ensure the following:
3.1. The names of programmatically created files contain only Latin letters and numbers. Text files are encoded to UTF-8 (to support macOS).
This requirement is applied to message exchange files, data export files, documents, and others, including archived files. This does not apply to files whose format cannot be altered. For example, files from other applications.
3.2. In scenarios, when a file name is user-defined, ensure that the users can use local language characters, and can transliterate the name to the Latin alphabet. It is recommended that by default the configuration prompts the users with Latin file names and encodes text files to UTF-8.
In the forms where users save files or choose encodings, provide tooltips with the information about the preferable names and encodings.
If the configuration contains Standard Subsystem Library, to convert Cyrillic letters to Latin letters, you can use the function StringFunctionsClientServer.LatinString.
Temporary files and directories
When utilizing temporary files and directories, comply with the following recommendations:
1. For the thin and thick clients, to get the name of a temporary file, use the method GetTempFileName. For the web client, see item 3.1. Otherwise, the users might face the following issues: the application malfunctions in the multi-user mode and when security profiles are enabled, insufficient rights to access system files, and unexpected growth of temporary files.
Incorrect:
InterimFileName = "C:\Temporary files 1С\TempFile.xml";
Data.Write(InterimFileName);
The current user might not have the right to write data to the directory. Also, if this code is running simultaneously in two different sessions, it will cause an error.
Correct:
InterimFileName = GetTempFileName("xml");
Data.Write(InterimFileName);
This guarantees access to the directory and uniqueness of the file name.
Also, GetTempFileName ensures that 1C:Enterprise will track and purge the temporary files. The files are deleted:
- When the user restarts the working process (for server-side files) or the application (for client-side files).
- 20 minutes after the server call or session in which the file was created (for 1C:Enterprise 8.3.17 or higher in client/server mode).
If the name of a temporary file was generated by other means, and the file was not deleted programmatically, this file cannot be tracked and purged. Unhandled temporary files will accumulate and might lead to disk space and performance issues, especially for infobases that have many users.
Incorrect:
Directory = TempFilesDir();
FileName = String(New UUID) + ".xml";
InterimFileName = Directory+ FileName;
Data.Write(InterimFileName);
Here, if the file is not deleted programmatically—for example, an exception is thrown—the file will be stored in the Temp directory indefinitely.
Correct:
InterimFileName = GetTempFileName("xml");
Data.Write(InterimFileName);
The GetTempFileName method ensures that 1C:Enterprise will track and purge the temporary files.
2. For the thin and thick clients, to generate the name for a temporary directory, employ GetTempFileName. For the web client, see item 3.1. This guarantees uniqueness of the file name and that 1C:Enterprise will delete the directory when the user restarts the working process or the application. A temporary directory can contain an unlimited number of directories and files.
3.1. Method GetTempFileName is unavailable for the web client. To generate a unique name for a temporary file or directory, employ the function TempFilesDir and the object UUID.
Incorrect:
Directory = TempFilesDir();
FileName = "TempDataFile.xml";
InterimFileName = Directory + FileName;
Data.Write(InterimFileName);
Correct:
Directory = TempFilesDir();
FileName = String(New UUID) + ".xml";
InterimFileName = Directory + FileName;
Data.Write(InterimFileName);
3.2. If the configuration contains Standard Subsystems Library, to create temporary directories on the client side, use the FileSystemClient.CreateTemporaryDirectory procedure.
4. Delete temporary files and directories that are no longer needed. Automatic purging must not be considered the primary approach to handle temporary files and directories. They might take up a lot of disk space.
InterimFileName = GetTempFileName("xml");
Data.Write(InterimFileName);
// Use the temp file
...
// Delete the temp file.
Try
DeleteFiles(InterimFileName);
Except
WriteLogEvent(NStr("en = 'My feature.Operation'"), EventLogLevel.Error, , , ErrorProcessing.DetailErrorDescription(ErrorInfo()));
EndTry;
See also: Event log
5. When temporary files and directories are used on the server, all necessary actions must be done in a single server call. Otherwise, for configurations that connect to a server cluster, these files might be unavailable if the next call is processed by another machine. To save data between two server calls, use the temporary storage (methods PutToTempStorage and GetFromTempStorage).
5.1. Sometimes, you might need to access a temporary file after the session end. For example, to prepare data for a background job required by a long-term operation that processes multiple web service calls. In this case, you need to provide shared storage space, grant the rights to access the files from all locations, and purge the files after they are no longer needed or if the process failed. Consider the following best practice:
- To provide access to the storage from all required locations, create a constant that stores the path to the storage available to all servers of the cluster.
- When a temporary file is created, its name and creation time are recorded to the information register.
- For errorless scenarios, the last operation deletes the temporary file and its record in the register.
- An ad-hoc scheduled task occasionally checks the register for the overdue records. If such records are found, the task deletes the temporary files and their records.
Client/server file transfer
1. If a file is used on both the client-side and server-side, transfer the file over temporary storage (methods PutFiles, GetFile, GetFiles, BeginPutFile, PutToTempStorage, and GetFromTempStorage). Usually, the client and the server have different file systems, where files are accessed by different users with their own access rights.
Incorrect:
&AtClient
Procedure ProcessFile()
...
FileName = "C:\Files to be processed\Import.xml";
Result = ProcessAtServer(FileName);
...
EndProcedure
&AtServer
Function ProcessAtServer(FileName)
Read = New TextReader(FileName);
...
Result = Read.Read();
Return Result;
EndFunction
Correct:
&AtClient
Procedure ProcessFile()
...
NameOfFileToProcess = "C:\Files to be processed\Import.xml";
NotifyDescription = New NotifyDescription(
"ProcessFileCompletion", ThisObject);
BeginPutFile(NotifyDescription,,
NameOfFileToProcess, False,
UUID);
EndProcedure
&AtClient
Procedure ProcessFileCompletion(Result, Address, SelectedFileName, AdditionalParameters)
...
Result = ProcessAtServer(Address);
...
EndProcedure
&AtServer
Function ProcessAtServer(Address)
Data = GetFromTempStorage(Address);
InterimFileName = GetTempFileName("txt");
Data.Write(InterimFileName);
Read = New TextReader(InterimFileName);
...
Result = Read.Read();
...
DeleteFiles(InterimFileName)
Return Result;
EndFunction
2. To save data to temporary storage between two server calls, pass the form ID to the parameter FormUniqueID of the PutFile method. The value is deleted when the user closes the form. If you need to save the same file to the temporary storage later, delete the previous value. Example:
Incorrect:
&AtClient
Procedure ProcessFile()
...
// First server call.
NameOfFileToProcess = "C:\Files to be processed\Import.xml";
NotifyDescription = New NotifyDescription(
"ProcessFileCompletion", ThisObject);
BeginPutFile(NotifyDescription,,
NameOfFileToProcess, False,
UUID);
...
EndProcedure
&AtClient
Procedure ProcessFileCompletion(Result, Address, SelectedFileName, AdditionalParameters)
...
Result = ExecuteInitialProcessingAtServer(Address);
ContinueFileProcessing();
...
EndProcedure
&AtClient
Procedure ContinueFileProcessing()
...
// Second server call with the same version of the file.
Result = ExecuteIntermediateProcessigAtServer(Address);
...
// Third server call with a new version of the file.
NotifyDescription = New NotifyDescription(
"ContinueFileProcessingCompletion", ThisObject);
BeginPutFile(NotifyDescription,,
NameOfFileToProcess, False,
UUID);
EndProcedure
&AtClient
Procedure ContinueFileProcessingCompletion(Result, Address, SelectedFileName, AdditionalParameters)
...
Result = ExecuteFinalProcessingAtServer(Address);
...
EndProcedure
Now, the temporary storage contains two copies of the same file. The address of the second copy is stored in the Address variable. The address of the first copy is lost. This results in the reduced disk space and performance.
Correct:
&AtClient
Procedure ProcessFile()
...
// First server call.
NameOfFileToProcess = "C:\Files to be processed\Import.xml";
NotifyDescription = New NotifyDescription(
"ProcessFileCompletion", ThisObject);
BeginPutFile(NotifyDescription,,
NameOfFileToProcess, False,
UUID);
...
EndProcedure
&AtClient
Procedure ProcessFileCompletion(Result, Address, SelectedFileName, AdditionalParameters)
...
Result = ExecuteInitialProcessingAtServer(Address);
ContinueFileProcessing();
...
EndProcedure
&AtClient
Procedure ContinueFileProcessing()
...
// Second server call with the same version of the file.
Result = ExecuteIntermediateProcessigAtServer(Address);
...
// Third server call with a new version of the file.
DeleteFromTempStorage(Address);
NotifyDescription = New NotifyDescription(
"ContinueFileProcessingCompletion", ThisObject);
BeginPutFile(NotifyDescription,,
NameOfFileToProcess, False,
UUID);
EndProcedure
&AtClient
Procedure ContinueFileProcessingCompletion(Result, Address, SelectedFileName, AdditionalParameters)
...
Result = ExecuteFinalProcessingAtServer(Address);
...
EndProcedure
3. If the configuration contains Standard Subsystems Library, to place files to a temporary storage, use the procedures ImportFile_ and ImportFiles of the FileSystemClient common module. To save file data between server calls, use the FormIdentifier property of the ImportParameters parameter:
&AtClient
Procedure ProcessFile()
...
NameOfFileToProcess = "C:\Files to be processed\Import.xml";
NotifyDescription = New NotifyDescription("ProcessFileCompletion", ThisObject);
ImportParameters = FileSystemClient.FileImportParameters();
ImportParameters.FormIdentifier = UUID;
ImportParameters.Interactively = False;
FileSystemClient.ImportFile_(NotifyDescription,
ImportParameters, NameOfFileToProcess);
EndProcedure
&AtClient
Procedure ProcessFileCompletion(FileThatWasPut, AdditionalParameters)
...
Result = ProcessAtServer(Address);
...
EndProcedure
See also: