190 lines
6.8 KiB
HTML
190 lines
6.8 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ru">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Загрузка и стриминг файла</title>
|
|
<style>
|
|
body { font-family: Arial, sans-serif; margin: 20px; }
|
|
section { margin-bottom: 40px; }
|
|
label { display: block; margin-bottom: 5px; }
|
|
input, button { margin-bottom: 10px; }
|
|
#log { border: 1px solid #ccc; padding: 10px; max-height: 200px; overflow-y: auto; background: #f9f9f9; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Загрузка и стриминг файла</h1>
|
|
|
|
<section id="uploadSection">
|
|
<h2>Загрузка файла</h2>
|
|
<label for="uploadFile">Выберите файл для загрузки:</label>
|
|
<input type="file" id="uploadFile">
|
|
<br>
|
|
<button id="uploadBtn">Загрузить файл</button>
|
|
<div id="uploadResult"></div>
|
|
</section>
|
|
|
|
<section id="streamSection">
|
|
<h2>Стриминг файла</h2>
|
|
<label for="fileHashInput">Введите file_hash:</label>
|
|
<input type="text" id="fileHashInput" placeholder="Введите hash">
|
|
<br>
|
|
<button id="loadFileBtn">Загрузить файл для стриминга</button>
|
|
<div id="mediaContainer" style="margin-top:20px;"></div>
|
|
</section>
|
|
|
|
<section id="logSection">
|
|
<h2>Лог</h2>
|
|
<div id="log"></div>
|
|
</section>
|
|
|
|
<script>
|
|
// Base URL for endpoints
|
|
const BASE_URL = "https://my-public-node-1.projscale.dev/api/v1.5/storage";
|
|
|
|
// Append log message to log div
|
|
function appendLog(message) {
|
|
const logDiv = document.getElementById('log');
|
|
const p = document.createElement('p');
|
|
p.textContent = message;
|
|
logDiv.appendChild(p);
|
|
}
|
|
|
|
// Upload file in chunks (max 80 MB per chunk) without computing file hash
|
|
async function uploadFileInChunks(file) {
|
|
const maxChunkSize = 80 * 1024 * 1024; // 80 MB
|
|
let offset = 0;
|
|
let uploadId = null;
|
|
appendLog("Starting file upload...");
|
|
|
|
while (offset < file.size) {
|
|
const chunk = file.slice(offset, Math.min(offset + maxChunkSize, file.size));
|
|
appendLog(`Uploading chunk starting at byte ${offset}`);
|
|
|
|
// Prepare headers for the chunk upload
|
|
const headers = {
|
|
"X-File-Name": btoa(unescape(encodeURIComponent(file.name))), // File name in base64
|
|
"X-Chunk-Start": offset.toString(),
|
|
"Content-Type": file.type || "application/octet-stream"
|
|
};
|
|
if (uploadId) {
|
|
headers["X-Upload-ID"] = uploadId;
|
|
}
|
|
// Set header to indicate the last chunk if this is the final part of the file
|
|
if (offset + chunk.size >= file.size) {
|
|
headers["X-Last-Chunk"] = "1";
|
|
}
|
|
|
|
const response = await fetch(BASE_URL, {
|
|
method: "POST",
|
|
headers: headers,
|
|
body: chunk
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
appendLog(`Chunk upload failed: ${errorData.error}`);
|
|
throw new Error(`Upload failed at offset ${offset}: ${errorData.error}`);
|
|
}
|
|
|
|
const resultData = await response.json();
|
|
// Save uploadId from first response if not set
|
|
if (!uploadId && resultData.upload_id) {
|
|
uploadId = resultData.upload_id;
|
|
}
|
|
|
|
// If final response contains content_id, upload is complete
|
|
if (resultData.content_id) {
|
|
appendLog(`Upload complete. File ID: ${resultData.content_id}`);
|
|
return resultData;
|
|
}
|
|
|
|
// Update offset based on server-reported current size
|
|
if (resultData.current_size !== undefined) {
|
|
offset = resultData.current_size;
|
|
appendLog(`Server reports current_size: ${offset}`);
|
|
} else {
|
|
appendLog("Unexpected response from server, missing current_size.");
|
|
throw new Error("Missing current_size in response");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Upload button event listener
|
|
document.getElementById('uploadBtn').addEventListener('click', async () => {
|
|
const fileInput = document.getElementById('uploadFile');
|
|
const uploadResult = document.getElementById('uploadResult');
|
|
uploadResult.textContent = "";
|
|
|
|
if (!fileInput.files || fileInput.files.length === 0) {
|
|
uploadResult.textContent = "Пожалуйста, выберите файл.";
|
|
return;
|
|
}
|
|
|
|
const file = fileInput.files[0];
|
|
try {
|
|
const resultData = await uploadFileInChunks(file);
|
|
uploadResult.textContent = `Файл загружен успешно. content_sha256: ${resultData.content_sha256}`;
|
|
} catch (err) {
|
|
uploadResult.textContent = "Ошибка при загрузке файла.";
|
|
}
|
|
});
|
|
|
|
// Load file for streaming (remains unchanged)
|
|
document.getElementById('loadFileBtn').addEventListener('click', async () => {
|
|
const fileHash = document.getElementById('fileHashInput').value.trim();
|
|
const mediaContainer = document.getElementById('mediaContainer');
|
|
mediaContainer.innerHTML = "";
|
|
|
|
if (!fileHash) {
|
|
mediaContainer.textContent = "Пожалуйста, введите file_hash.";
|
|
return;
|
|
}
|
|
|
|
const fileUrl = `${BASE_URL}/${fileHash}`;
|
|
appendLog(`Fetching file info for hash: ${fileHash}`);
|
|
|
|
try {
|
|
const headResponse = await fetch(fileUrl, { method: "HEAD" });
|
|
if (!headResponse.ok) {
|
|
mediaContainer.textContent = "Файл не найден.";
|
|
appendLog("File not found during HEAD request.");
|
|
return;
|
|
}
|
|
|
|
const contentType = headResponse.headers.get("Content-Type") || "";
|
|
appendLog(`Content-Type: ${contentType}`);
|
|
|
|
let mediaElement;
|
|
if (contentType.startsWith("image/")) {
|
|
mediaElement = document.createElement("img");
|
|
mediaElement.style.maxWidth = "100%";
|
|
} else if (contentType.startsWith("video/")) {
|
|
mediaElement = document.createElement("video");
|
|
mediaElement.controls = true;
|
|
mediaElement.style.maxWidth = "100%";
|
|
} else if (contentType.startsWith("audio/")) {
|
|
mediaElement = document.createElement("audio");
|
|
mediaElement.controls = true;
|
|
} else {
|
|
mediaElement = document.createElement("a");
|
|
mediaElement.textContent = "Скачать файл";
|
|
}
|
|
|
|
if (mediaElement.tagName === "A") {
|
|
mediaElement.href = fileUrl;
|
|
mediaElement.download = "";
|
|
} else {
|
|
mediaElement.src = fileUrl;
|
|
}
|
|
|
|
mediaContainer.appendChild(mediaElement);
|
|
appendLog("Media element created and added to the page.");
|
|
} catch (err) {
|
|
mediaContainer.textContent = "Ошибка при загрузке файла.";
|
|
appendLog(`Error during file streaming: ${err}`);
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|