ifyour/cf-image-hosting

文件托管到哪儿了?

Closed this issue · 9 comments

文件上传后,如何查看历史文件呢?
拜托详细一点点的说明一下。

看上去还不支持,close

cf-pages/Telegraph-Image#116 (comment)
类似项目有后台功能

要是能扩充后台功能成一个图库站点就更好了。

image

https://upimg.wook.eu.org/
简单改了一个使用本地存储历史记录的

index.html:

<!-- index.html -->
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Telegraph Image Hosting</title>
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.5.3/css/bootstrap.min.css"
    />
    <link rel="stylesheet" href="./style.css" />
  </head>

  <body>
    <div class="full-window" ondragover="event.preventDefault()">
      <div class="container card">
        <h3 class="text-center">Telegraph Image Hosting</h3>
        <p class="text-center text-muted">Free & Unlimited Image Hosting</p>
        <button
          id="upload"
          class="btn btn-primary mx-auto"
          type="button"
          title="Supported formats: Images, videos, GIFs"
        >
          <span class="spinner-grow spinner-grow-sm d-none"></span>
          <span class="upload-text"
            >Drag & Drop your files or <u><i>Browse</i></u></span
          >
          <input
            id="fileInput"
            type="file"
            name="file"
            accept="image/*, video/*"
          />
        </button>
        <div
          id="uploadStatus"
          class="text-center"
          style="margin-top: 10px"
        ></div>
        <a
          class="text-center text-muted"
          href="https://github.com/ifyour/cf-image-hosting"
          target="_blank"
          >GitHub</a
        >
      </div>
    </div>

    <input type="text" id="usernameInput" placeholder="Enter Category Tags" style="position: absolute; top: 10px; right: 100px;">
    <button id="listImagesButton" style="position: absolute; top: 10px; right: 10px;">列出记录</button>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/browser-image-compression/2.0.2/browser-image-compression.min.js"></script>
    <script src="./script.js"></script>
  </body>
</html>

script.js

const fileInput = document.getElementById("fileInput");
const uploadStatus = document.getElementById("uploadStatus");
const fullWindow = document.querySelector(".full-window");

document.addEventListener("paste", onFilePaste);
fullWindow.addEventListener("drop", onFileDrop);
fileInput.addEventListener("change", onFileChange);

// 列出按钮执行动作后,在页面中添加一个输出框显示结果
document.getElementById('listImagesButton').addEventListener('click', () => {
    const username = document.getElementById('usernameInput').value;
    const savedValues = localStorage.getItem(username);
    
    const outputDiv = document.createElement('div');
    outputDiv.innerHTML = `<h4>${username}'s Saved Images:</h4>`;
    
    // 创建一个文本区域元素以显示图片URL
    const textarea = document.createElement('textarea');
    textarea.rows = 5; // 可以根据需要调整行数
    textarea.cols = 60; // 可以根据需要调整列数
    
    // 将文本区域添加到outputDiv的顶部
    outputDiv.appendChild(textarea);
    
    if (savedValues) {
        const images = savedValues.split(';').filter(Boolean);
        images.forEach(src => {
            // 将图片URL添加到文本区域,并确保使用正确的换行符
            textarea.value += src + '\n'; // 这里使用 '\n' 来换行
        });
        
        images.forEach((src, index) => {
            const imgElement = document.createElement('img');
            imgElement.src = src;
            imgElement.style.maxWidth = '200px';
            // 将图片元素添加到文本区域下方
            outputDiv.appendChild(imgElement);
        });
    } else {
        outputDiv.innerHTML += `<p>No images saved for ${username}</p>`;
    }
    
    document.body.prepend(outputDiv);
});



function onFileChange() {
  const file = fileInput.files[0];
  if (file) handleUpload(file);
}

function onFileDrop(event) {
  event.preventDefault();
  let files = event.dataTransfer.files;
  for (let i = 0; i < files.length; i++) {
    handleUpload(files[i]);
  }
}

function onFilePaste(event) {
  const items = (event.clipboardData || event.originalEvent.clipboardData)
    .items;
  for (let index in items) {
    const item = items[index];
    if (item.kind === "file") {
      const blob = item.getAsFile();
      const reader = new FileReader();
      reader.onload = (event) => {
        const base64Data = event.target.result.split(",")[1];
        const dataType = event.target.result.split(";")[0];
        const fileType = dataType.split(":")[1];
        const data = window.atob(base64Data);
        const ia = new Uint8Array(data.length);
        for (let i = 0; i < data.length; i++) {
          ia[i] = data.charCodeAt(i);
        }
        const blob = new Blob([ia.buffer], { type: fileType });
        const file = new File([blob], "screenshot.jpg", { type: fileType });
        handleUpload(file);
      };
      reader.readAsDataURL(blob);
    }
  }
}

function onFileUrlCopy() {
  const imageUrl = document.getElementById("imageUrl");
  navigator.clipboard
    .writeText(imageUrl.value)
    .then(() => {
      document.querySelector(".copy-btn").textContent = "Copied ✨";
      setTimeout(() => {
        document.querySelector(".copy-btn").textContent = "Copy";
      }, 1000);
    })
    .catch((error) => {
      console.error("Failed to copy URL", error);
    });
}

function handleCompressFile(file) {
  const maxFileSize = 5 * 1024 * 1024; // 5MB
  return new Promise((resolve) => {
    if (file.size <= maxFileSize || !file.type.startsWith("image")) {
      resolve(file);
    } else {
      imageCompression(file, { maxSizeMB: 5 })
        .then((compressedFile) => {
          resolve(compressedFile);
        })
        .catch((error) => {
          console.error(">> imageCompression error", error);
          resolve(file);
        });
    }
  });
}

function handleUpload(file) {
  document.querySelector(".upload-text").textContent = "Uploading...";
  document.querySelector(".spinner-grow").classList.remove("d-none");
  handleCompressFile(file).then((compressedFile) => {
    const formData = new FormData();
    formData.append("file", compressedFile);
    fetch("/upload", { method: "POST", body: formData })
      .then((response) => response.json())
      .then((data) => {
        if (data && data.error) {
          throw new Error(data.error);
        }
        const src = window.location.origin + data[0].src;
        const newSrc = src.replace(window.location.origin, 'https://imghost.wook.eu.org');
        fetch(newSrc)
          .then((response) => {
            // 处理后台访问链接的逻辑
          })
          .catch((error) => {
            console.error("Failed to access the modified URL", error);
          });
        uploadStatus.innerHTML = `
        <div class="alert alert-success text-center">Successful 🥳</div>
        <div class="input-group" style="margin-bottom: 10px">
          <input type="text" class="form-control" id="imageUrl" value="${src}">
          <div class="input-group-append">
            <button class="btn btn-outline-secondary copy-btn" type="button">Copy</button>
<button id="saveButton" class="btn btn-primary">Save</button>
          </div>
        </div>
        ${
          file.type.startsWith("video")
            ? `<video src="${src}" class="img-fluid mb-3" controls></video>`
            : `<img src="${src}" class="img-fluid mb-3" alt="Uploaded Image">`
        }
        `;
// 保存按钮执行保存动作
    document.getElementById('saveButton').addEventListener('click', () => {
    const username = document.getElementById('usernameInput').value;
    const src = document.getElementById('imageUrl').value;
    const existingValue = localStorage.getItem(username) || '';
    localStorage.setItem(username, existingValue + src + ';');
});
        document
          .querySelector(".copy-btn")
          .addEventListener("click", onFileUrlCopy);
      })
      .catch((error) => {
        uploadStatus.innerHTML = `
        <div class="alert alert-danger">${
          error || "Upload failed. Please try again."
        }</div>
        `;
      })
      .finally(() => {
        document.querySelector(".upload-text").textContent = "Upload Again";
        document.querySelector(".spinner-grow").classList.add("d-none");
      });
  });
}

增加了历史URL地址输出,便于复制保存。

抱歉,最近有点忙,没时间搞,你可以 fork 一份部署 👍🏻

image
让AI写了一个相册管理站点,php的

<?php
session_start(); // 开始会话

$albumDir = 'albums/';
$albums = glob($albumDir . '*.txt');
$correctPassword = 'addroot'; // 设置固定的口令

// 处理备份相册的操作
if (isset($_POST['backup']) && isset($_SESSION['password']) && $_SESSION['password'] === $correctPassword) {
    $zip = new ZipArchive();
    $backupFileName = $albumDir . 'backup_' . date('YmdHis') . '.zip';
    if ($zip->open($backupFileName, ZipArchive::CREATE) === TRUE) {
        foreach ($albums as $album) {
            $zip->addFile($album, basename($album));
        }
        $zip->close();

        // 触发下载
        header('Content-Type: application/zip');
        header('Content-Disposition: attachment; filename="' . basename($backupFileName) . '"');
        header('Content-Length: ' . filesize($backupFileName));
        ob_clean(); // 清除缓冲区
        flush(); // 刷新输出缓冲
        readfile($backupFileName);
        // 删除服务器上的备份文件
        unlink($backupFileName);
        exit;
    } else {
        echo "<p>备份失败。</p>";
    }
}

// 检查会话中的口令或表单提交的口令
if (isset($_SESSION['password']) && $_SESSION['password'] === $correctPassword) {
    // 口令正确,显示管理界面

    // 显示备份按钮和返回首页按钮
    echo "<div style='display: flex; align-items: center; justify-content: space-between; margin-bottom: 20px;'>";
    echo "<a href='index.php' style='text-decoration: none;'>";
    echo "<button type='button'>返回首页</button>";
    echo "</a>";
    echo "<form method='post'>";
    echo "<input type='submit' name='backup' value='备份相册'>";
    echo "</form>";
    echo "</div>";

    // 处理创建新相册的操作
    if (isset($_POST['create']) && !empty($_POST['newAlbumName'])) {
        $newAlbumName = trim($_POST['newAlbumName']);
        $newAlbumFile = $albumDir . $newAlbumName . '.txt';
        if (!file_exists($newAlbumFile)) {
            file_put_contents($newAlbumFile, '');
            echo "<p>新相册 '{$newAlbumName}' 创建成功。</p>";
        } else {
            echo "<p>相册 '{$newAlbumName}' 已存在。</p>";
        }
    }

    // 如果设置了album参数,显示相册内容管理界面
    if (isset($_GET['album'])) {
        $albumName = basename($_GET['album'], '.txt');
        $albumFile = $albumDir . $albumName . '.txt';

        // 显示相册内容管理界面
        echo "<h2>管理相册: $albumName</h2>";
        echo "<a href='?'>返回相册列表</a><br>";

        // 处理增加图片的操作
        if (isset($_POST['add'])) {
            $imageUrls = explode("\n", $_POST['imageUrls']); // 从文本区域获取多个URL
            foreach ($imageUrls as $imageUrl) {
                if (!empty($imageUrl)) {
                    file_put_contents($albumFile, trim($imageUrl) . "\n", FILE_APPEND);
                }
            }
        }

        // 处理删除图片的操作
        if (isset($_POST['delete'])) {
            $imageUrl = $_POST['imageUrl'];
            $images = file($albumFile, FILE_IGNORE_NEW_LINES);
            $images = array_filter($images, function ($line) use ($imageUrl) {
                return trim($line) !== trim($imageUrl);
            });
            file_put_contents($albumFile, implode("\n", $images) . "\n");
        }

        // 显示图片和删除按钮
        $images = file($albumFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        echo "<div style='display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px;'>";
        foreach ($images as $imageUrl) {
            echo "<div style='text-align: center;'>";
            echo "<a href='$imageUrl'><img src='$imageUrl' alt='Thumbnail' width='100' height='100'></a>";
            echo "<form method='post' style='display: inline;' onsubmit='return confirmDelete();'>";
            echo "<input type='hidden' name='imageUrl' value='$imageUrl'>";
            echo "<input type='submit' name='delete' value='删除'>";
            echo "</form>";
            echo "</div>";
        }
        echo "</div>";

        // 显示添加图片表单
        echo "<form method='post' style='margin-top: 20px;'>";
        echo "<textarea name='imageUrls' placeholder='输入图片URL,每行一个' style='width: 500px; height: 100px;'></textarea>";
        echo "<input type='submit' name='add' value='批量添加图片'>";
        echo "</form>";

    } else {
        // 显示相册列表
        echo "<h2>相册列表</h2>";
        echo "<div style='display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px;'>";
        foreach ($albums as $album) {
            $albumName = basename($album, '.txt');
            echo "<a href='?album=$albumName' style='text-align: center;'>$albumName</a>";
        }
        echo "</div>";

        // 显示创建新相册表单
        echo "<form method='post' style='margin-top: 20px;'>";
        echo "<input type='text' name='newAlbumName' placeholder='输入新相册名称'>";
        echo "<input type='submit' name='create' value='创建新相册'>";
        echo "</form>";
    }

} elseif (isset($_POST['password']) && $_POST['password'] === $correctPassword) {
    // 口令正确,保存口令到会话
    $_SESSION['password'] = $_POST['password'];
    // 重定向到相同页面,避免表单重复提交
    header('Location: ' . $_SERVER['PHP_SELF']);
    exit;
} else {
    // 显示口令输入表单
    echo "<form method='post'>";
    echo "<input type='password' name='password' placeholder='输入口令'>";
    echo "<input type='submit' value='提交'>";
    echo "</form>";
}
?>
<script>
// JavaScript函数用于在删除操作前确认
function confirmDelete() {
    return confirm('确定要删除这张图片吗?');
}
</script>
<!-- HTML 内容 -->
</body>
</html>

换到 pages 部署了,已支持历史上传记录。🤪

换到 pages 部署了,已支持历史上传记录。🤪

代码没提交更新吗?

其实打算在上面相册管理站点增加一个图片上传功能,调用这个项目的API,我代码小白,不知道这个功能该如何向AI描述让AI来完善上面站点代码。
功能就是:点击批量增加图片,如果输入框中没有URL地址,则打开文件管理器,选择图片(可以多选),调用API上传,接收上传返回的URL地址(多选需要保留全部),代替文本框的输入的值,完成批量图片增加。

测试了一下部署在workers上的API,有CORS限制;
@ifyour 可否开放部署的CORS,以便于其它网页调用上传功能?

测试了一下部署在workers上的API,有CORS限制; @ifyour 可否开放部署的CORS,以便于其它网页调用上传功能?

3e81fe9 安排了,这个 issue 先关了 🌚