Реалізація доступу до файлової системи у десктоп-застосунку

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація доступу до файлової системи у десктоп-застосунку
Середня
~2-3 робочих дні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація доступу до файлової системи в десктоп-приложенні

Робота з файловою системою — одне з головних переваг десктоп-приложення перед веб-версією. Electron дає повний доступ через Node.js API; Tauri — через Rust-команди з явними дозволами.

Electron: сервіс файлової системи

// main/fs-service.js
const fs = require('fs/promises');
const path = require('path');
const { app, dialog } = require('electron');

class FileSystemService {
  async openFileDialog(win, options = {}) {
    const result = await dialog.showOpenDialog(win, {
      properties: ['openFile'],
      filters: options.filters ?? [{ name: 'All Files', extensions: ['*'] }],
      ...options
    });
    if (result.canceled) return null;
    return this.readFile(result.filePaths[0]);
  }

  async openFolderDialog(win) {
    const result = await dialog.showOpenDialog(win, {
      properties: ['openDirectory']
    });
    if (result.canceled) return null;
    return result.filePaths[0];
  }

  async readFile(filePath) {
    const stat = await fs.stat(filePath);
    if (stat.size > 50 * 1024 * 1024) {
      throw new Error(`File too large: ${(stat.size / 1024 / 1024).toFixed(1)} MB`);
    }
    const content = await fs.readFile(filePath, 'utf-8');
    return {
      path: filePath,
      name: path.basename(filePath),
      ext: path.extname(filePath).slice(1),
      content,
      size: stat.size,
      modified: stat.mtimeMs
    };
  }

  async saveFile(win, content, currentPath = null) {
    let savePath = currentPath;
    if (!savePath) {
      const result = await dialog.showSaveDialog(win, {
        defaultPath: path.join(app.getPath('documents'), 'untitled.txt')
      });
      if (result.canceled) return null;
      savePath = result.filePath;
    }
    await fs.writeFile(savePath, content, 'utf-8');
    return savePath;
  }

  async listDirectory(dirPath, options = {}) {
    const entries = await fs.readdir(dirPath, { withFileTypes: true });
    const items = await Promise.all(
      entries
        .filter(e => options.showHidden || !e.name.startsWith('.'))
        .map(async (entry) => {
          const fullPath = path.join(dirPath, entry.name);
          try {
            const stat = await fs.stat(fullPath);
            return {
              name: entry.name,
              path: fullPath,
              isDirectory: entry.isDirectory(),
              size: entry.isFile() ? stat.size : 0,
              modified: stat.mtimeMs
            };
          } catch {
            return null;
          }
        })
    );
    return items.filter(Boolean);
  }

  watchFile(filePath, callback) {
    const watcher = require('fs').watch(filePath, (eventType) => {
      callback({ eventType, path: filePath });
    });
    return () => watcher.close();
  }

  getAppPaths() {
    return {
      userData: app.getPath('userData'),
      documents: app.getPath('documents'),
      downloads: app.getPath('downloads'),
      temp: app.getPath('temp'),
      home: app.getPath('home')
    };
  }
}

module.exports = new FileSystemService();

Drag & Drop файлів

// renderer — обробка drop
const dropZone = document.getElementById('drop-zone');

dropZone.addEventListener('dragover', (e) => {
  e.preventDefault();
  dropZone.classList.add('dragging');
});

dropZone.addEventListener('drop', async (e) => {
  e.preventDefault();
  dropZone.classList.remove('dragging');

  const files = Array.from(e.dataTransfer.files).map(f => ({
    name: f.name,
    path: f.path, // Electron додає .path
    size: f.size,
    type: f.type
  }));

  for (const file of files) {
    const content = await window.electronAPI.fs.readFile(file.path);
    handleFile(content);
  }
});

Tauri: файлова система через плагін

# src-tauri/Cargo.toml
[dependencies]
tauri-plugin-fs = "2"
tauri-plugin-dialog = "2"
// renderer/api/fs.ts
import { readTextFile, writeTextFile, readDir } from '@tauri-apps/plugin-fs';
import { open, save } from '@tauri-apps/plugin-dialog';

export async function openAndReadFile() {
  const selected = await open({
    multiple: false,
    filters: [{ name: 'Text', extensions: ['txt', 'md', 'json'] }]
  });
  if (!selected) return null;
  const content = await readTextFile(selected as string);
  return { path: selected as string, content };
}

export async function saveToFile(content: string, currentPath?: string) {
  const filePath = currentPath ?? await save({
    filters: [{ name: 'Text', extensions: ['txt'] }]
  });
  if (!filePath) return null;
  await writeTextFile(filePath as string, content);
  return filePath;
}

Безпека: ніколи не довіряйте шляхам з renderer без валідації в main process. Path traversal (../../etc/passwd) — реальний вектор атаки в Electron-приложеннях, які рендерять удалений контент.