first commit
This commit is contained in:
75
README.md
Normal file
75
README.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# Minimal Chunked File Storage (PHP)
|
||||||
|
|
||||||
|
This is a minimalistic PHP-based file storage system with:
|
||||||
|
|
||||||
|
* Password-protected admin panel
|
||||||
|
* Chunked file upload for large files (GB+)
|
||||||
|
* Upload progress display
|
||||||
|
* File listing with download and delete options
|
||||||
|
* Direct link copy button
|
||||||
|
* No external libraries required
|
||||||
|
|
||||||
|
## 🚀 Features
|
||||||
|
|
||||||
|
* Chunked upload via JavaScript + `fetch`
|
||||||
|
* Server-side chunk handling and merging
|
||||||
|
* No CSS or frontend frameworks
|
||||||
|
* Fully self-contained (no Composer needed)
|
||||||
|
|
||||||
|
## 🛠 Requirements
|
||||||
|
|
||||||
|
* PHP 7.4+
|
||||||
|
* A web server (or use PHP's built-in server)
|
||||||
|
|
||||||
|
## 📁 Folder Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
/simple-file-share/
|
||||||
|
├── index.php
|
||||||
|
├── upload/ # Final uploaded files
|
||||||
|
├── upload_chunks/ # Temporary chunk storage
|
||||||
|
├── upload_chunk.php
|
||||||
|
├── merge_chunks.php
|
||||||
|
└── config.php # Configuration (password and paths)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔐 Configuration
|
||||||
|
|
||||||
|
Create `config.php`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
define('PASSWORD', 'your_password_here');
|
||||||
|
define('UPLOAD_DIR', __DIR__ . '/upload/');
|
||||||
|
define('CHUNK_DIR', __DIR__ . '/upload_chunks/');
|
||||||
|
```
|
||||||
|
|
||||||
|
## ▶️ Run the Server
|
||||||
|
|
||||||
|
Use PHP's built-in server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php -S localhost:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📤 Uploading Files
|
||||||
|
|
||||||
|
* Select a file in the UI.
|
||||||
|
* It is split into 1MB chunks (adjustable in JS).
|
||||||
|
* Upload progress is shown.
|
||||||
|
* File is merged server-side and available for download.
|
||||||
|
|
||||||
|
## 🧹 Cleanup
|
||||||
|
|
||||||
|
* Temporary chunk files are automatically deleted after merging.
|
||||||
|
* Files can be deleted via the admin interface.
|
||||||
|
|
||||||
|
## 📝 License
|
||||||
|
|
||||||
|
This project is public domain / MIT – use freely.
|
||||||
4
config.php
Normal file
4
config.php
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
define('PASSWORD', 'your_password_here');
|
||||||
|
define('UPLOAD_DIR', __DIR__ . '/upload/');
|
||||||
|
define('CHUNK_DIR', __DIR__ . '/upload_chunks/');
|
||||||
87
index.php
Normal file
87
index.php
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/config.php';
|
||||||
|
|
||||||
|
if (!file_exists(UPLOAD_DIR)) mkdir(UPLOAD_DIR);
|
||||||
|
if (!file_exists(CHUNK_DIR)) mkdir(CHUNK_DIR);
|
||||||
|
|
||||||
|
if (!isset($_SESSION['logged_in']) || ($_GET['action'] ?? '') === 'logout') {
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['password'] ?? '') === PASSWORD) {
|
||||||
|
$_SESSION['logged_in'] = true;
|
||||||
|
header("Location: index.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<form method="post">
|
||||||
|
<h3>Login</h3>
|
||||||
|
<input type="password" name="password" placeholder="Password">
|
||||||
|
<button type="submit">Login</button>
|
||||||
|
</form>';
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаление
|
||||||
|
if (isset($_GET['delete'])) {
|
||||||
|
$f = basename($_GET['delete']);
|
||||||
|
@unlink(UPLOAD_DIR . $f);
|
||||||
|
header("Location: index.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$files = array_diff(scandir(UPLOAD_DIR), ['.', '..']);
|
||||||
|
?><!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head><meta charset="utf-8"><title>Файловое хранилище</title></head>
|
||||||
|
<body>
|
||||||
|
<h3>Файловое хранилище</h3>
|
||||||
|
<p><a href="?action=logout">Выйти</a></p>
|
||||||
|
<input type="file" id="fileInput">
|
||||||
|
<div id="progress"></div>
|
||||||
|
<ul>
|
||||||
|
<?php foreach ($files as $file): $url = 'upload/' . rawurlencode($file); ?>
|
||||||
|
<li>
|
||||||
|
<?= htmlspecialchars($file) ?>
|
||||||
|
[<a href="<?= $url ?>" target="_blank">Скачать</a>]
|
||||||
|
[<a href="?delete=<?= urlencode($file) ?>" onclick="return confirm('Удалить?')">Удалить</a>]
|
||||||
|
<button onclick="copyLink('<?= $url ?>')">Копировать ссылку</button>
|
||||||
|
</li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function copyLink(link) {
|
||||||
|
navigator.clipboard.writeText(location.origin + '/' + link).then(() => alert("Скопировано"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const input = document.getElementById('fileInput');
|
||||||
|
input.addEventListener('change', async () => {
|
||||||
|
const file = input.files[0];
|
||||||
|
if (!file) return;
|
||||||
|
const chunkSize = 1024 * 1024; // 1MB
|
||||||
|
const totalChunks = Math.ceil(file.size / chunkSize);
|
||||||
|
|
||||||
|
for (let i = 0; i < totalChunks; i++) {
|
||||||
|
const chunk = file.slice(i * chunkSize, (i + 1) * chunkSize);
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('chunk', chunk);
|
||||||
|
formData.append('filename', file.name);
|
||||||
|
formData.append('index', i);
|
||||||
|
formData.append('total', totalChunks);
|
||||||
|
|
||||||
|
await fetch('upload_chunk.php', { method: 'POST', body: formData });
|
||||||
|
document.getElementById('progress').innerText = `Загружено: ${i+1}/${totalChunks}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await fetch('merge_chunks.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ filename: file.name, total: totalChunks })
|
||||||
|
});
|
||||||
|
|
||||||
|
alert('Готово!');
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
21
merge_chunks.php
Normal file
21
merge_chunks.php
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once __DIR__ . '/config.php';
|
||||||
|
|
||||||
|
$data = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$filename = basename($data['filename']);
|
||||||
|
$total = intval($data['total']);
|
||||||
|
$tmpDir = __DIR__ . '/upload_chunks/' . $filename;
|
||||||
|
$finalPath = __DIR__ . '/upload/' . $filename;
|
||||||
|
|
||||||
|
$out = fopen($finalPath, 'w');
|
||||||
|
for ($i = 0; $i < $total; $i++) {
|
||||||
|
$partPath = "$tmpDir/$i.part";
|
||||||
|
$in = fopen($partPath, 'r');
|
||||||
|
stream_copy_to_stream($in, $out);
|
||||||
|
fclose($in);
|
||||||
|
unlink($partPath);
|
||||||
|
}
|
||||||
|
fclose($out);
|
||||||
|
rmdir($tmpDir);
|
||||||
|
http_response_code(200);
|
||||||
8
upload_chunk.php
Normal file
8
upload_chunk.php
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$filename = basename($_POST['filename']);
|
||||||
|
$index = intval($_POST['index']);
|
||||||
|
$tmpDir = __DIR__ . '/upload_chunks/' . $filename;
|
||||||
|
if (!file_exists($tmpDir)) mkdir($tmpDir, 0777, true);
|
||||||
|
|
||||||
|
move_uploaded_file($_FILES['chunk']['tmp_name'], "$tmpDir/$index.part");
|
||||||
Reference in New Issue
Block a user