[Unity (C #)] Implementation that sets a file storage limit and deletes files in order from the oldest date

4 minute read

Introduction

↑ This is the previous application.

What I want to do is as shown in the image below
Set a file storage limit,
The implementation is to delete the oldest file when the limit is exceeded.
SaveLimitImage.png

demo

It’s a demo right away.
The file to be saved is an image file.
Paint2DForQiita5.gif

When I checked the storage area in the PC,
The last 3 sheets were firmly preserved.

SaveImageCheckSS.PNG

code

Below is the full text of this process.

Attach to a suitable object


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UniRx;
using UniRx.Triggers;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
///Texture ⇔ Png Image conversion, saving and loading
/// </summary>
public class TexturePngConverter : MonoBehaviour
{
    [SerializeField] private Button _saveButton;
    [SerializeField] private Button _loadButton;
    [SerializeField] private Image _paintImage;
    [SerializeField] private Painter _painter;
    [SerializeField] private Transform _loadImagesParentTransform;

    private const string IMAGE_SAVE_FOLDER = "Image";
    private const string PNG = ".png ";

    /// <summary>
    ///Limit storage number
    /// </summary>
    private const int UPPER_LIMIT_SAVE_PICTURE = 3;
    
    private void Start()
    {
        //Save button
        _saveButton.OnPointerClickAsObservable()
            .Subscribe(_ =>
            {
                //Save
                ConvertToPngAndSave(GetSaveDirectoryPath(IMAGE_SAVE_FOLDER), GetSaveFilePath(IMAGE_SAVE_FOLDER, PNG));
                //reset
                _painter.ResetTexture();
            }).AddTo(this);

        //Load button
        _loadButton.OnPointerClickAsObservable()
            .Subscribe(_ => ConvertToTextureAndLoad(GetSaveDirectoryPath(IMAGE_SAVE_FOLDER))).AddTo(this);
    }

    /// <summary>
    ///Get the path of the save destination file
    /// </summary>
    /// <param name="folderName">Delimited folder name</param>
    /// <param name="fileType">extension</param>
    /// <returns>Destination path</returns>
    private string GetSaveFilePath(string folderName, string fileType)
    {
        return GetSaveDirectoryPath(folderName) + DateTime.Now.ToString("yyyyMMddHHmmss") + fileType;
    }

    ///Get the path of the save destination directory
    /// </summary>
    /// <param name="folderName">Delimited folder name</param>
    /// <returns>Destination path</returns>
    private string GetSaveDirectoryPath(string folderName)
    {
        string directoryPath = Application.persistentDataPath + "/" + folderName + "/";

        if (!Directory.Exists(directoryPath))
        {
            //Create if it doesn't exist yet
            Directory.CreateDirectory(directoryPath);
            return directoryPath;
        }

        return directoryPath;
    }

    /// <summary>
    /// "Files under the directory"Returns a list containing all
    ///The oldest file is[0]Th
    /// </summary>
    /// <param name="directoryName">Parent directory of files you want to get</param>
    /// <returns>A list containing all the files under the specified directory</returns>
    private List<string> GetAllFileFromDirectory(string directoryName)
    {
        //Sort files so that the oldest one comes first
        List<string> imageFilePathList = Directory
            //Get all files in the Image directory
            .GetFiles(directoryName, "*", SearchOption.AllDirectories)
            //.DS_Excludes Store
            .Where(filePath => Path.GetFileName(filePath) != ".DS_Store")
            //Sort in descending order by date
            .OrderBy(filePath => File.GetLastWriteTime(filePath).Date)
            //Sort in descending order by time within the same date
            .ThenBy(filePath => File.GetLastWriteTime(filePath).TimeOfDay)
            .ToList();

        return imageFilePathList;
    }

    /// <summary>
    ///Convert & save to image
    ///Check the maximum number of saved items
    /// </summary>
    /// <param name="directoryPath">Directory to check the number of saved</param>
    /// <param name="fileSavePath">Destination path</param>
    private void ConvertToPngAndSave(string directoryPath, string fileSavePath)
    {
        //Get a list containing all the files under the specified directory
        List<string> imageFilePaths = GetAllFileFromDirectory(directoryPath);

        //Check the maximum number of files
        if (imageFilePaths.Count >= UPPER_LIMIT_SAVE_PICTURE)
        {
            //If the limit is reached, delete the oldest file
            File.Delete(imageFilePaths[0]);
        }

        //Convert to png
        byte[] bytes = _paintImage.sprite.texture.EncodeToPNG();
        //Save
        File.WriteAllBytes(fileSavePath, bytes);
    }

    /// <summary>
    ///Convert & load to texture
    /// </summary>
    /// <param name="directoryPath">Parent directory of files you want to load</param>
    private void ConvertToTextureAndLoad(string directoryPath)
    {
        List<Image> imageList = new List<Image>();

        //Create an Image list to display multiple images after loading
        foreach (Transform child in _loadImagesParentTransform)
        {
            Image childImage = child.gameObject.GetComponent<Image>();

            if (childImage != null)
            {
                imageList.Add(childImage);
            }
        }
        
        //Get a list containing all the files under the specified directory
        List<string> imageFilePaths = GetAllFileFromDirectory(directoryPath);

        //Index counter
        int count = 0;

        //Load from the list of files in chronological order and apply to Image
        foreach (string imageFilePath in imageFilePaths)
        {
            Debug.Log(imageFilePath);
            //Read
            byte[] bytes = File.ReadAllBytes(imageFilePath);
            //Convert image to texture
            Texture2D loadTexture = new Texture2D(2, 2);
            loadTexture.LoadImage(bytes);
            //Convert textures to sprites
            imageList[count].sprite = Sprite.Create(loadTexture, new Rect(0, 0, loadTexture.width, loadTexture.height), Vector2.zero);
            //Advance the index counter
            count++;
        }
    }
}

sort

This time, it is ** the date and time (in seconds) where the concept of the old folder was saved **.
Therefore, from the files in the directory
I needed to get the ** oldest save date file **.

For that purpose, get all the files once
The paths of each save destination are sorted in the order of save.

When loading, get the path of all files in the same way
It is passed to the argument of File.ReadAllBytes.

I was able to write easily using LINQ by referring to the link below.

DateTime.Now
You can use DateTime.Now to get the current date and the time in seconds.

In this implementation,
By using this date and the time up to the second as the file name
Generating a unique path.

However, when I gave it as a file name as it was, it was a little troublesome.

Interpreted as a directory delimiter like ʻImage / 2020/09/21 17: 12: 43.png / (Slash)` will be included.

If you try to save as it is
** I get angry that such a directory does not exist **.

Therefore, we are passing " yyyyMMddHHmmss " as an argument as shown below.

DateTime.Now.ToString("yyyyMMddHHmmss")

By doing this, it becomes ʻImage / 20200921171710.png`
It is possible to get all numbers up to the number of seconds.


2020/09/21 postscript

GetFiles(directoryName, "*", SearchOption.AllDirectories)
It is better to change the part of to " * "" * .png " etc.
I received a comment that you can exclude unexpected files.
I will make a note (Thank you!)

in conclusion

No processing failure was seen even when 5,60 sheets were read at once on a mobile terminal.
When the number of sheets reaches hundreds or thousands, it seems that various ingenuity is required.