You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于日期删除文件系统目录:Phoenix中SeaweedFS图片删除端点开发

Hey there! Let's walk through how to build this deletion endpoint in Phoenix for your SeaweedFS setup, since you already have the snapshot fetching logic in place. Here's a step-by-step breakdown tailored to your directory structure:

Step 1: Convert Timestamps to Directory Prefixes

First, you need to map the start/end timestamps to the corresponding directory paths in SeaweedFS. Your structure uses Camera-name/recordings/year/month/day/hour/, so we'll convert Unix timestamps into these components and build prefixes to target.

Add this helper function to a utility module (like YourApp.SeaweedFS.Utils):

defmodule YourApp.SeaweedFS.Utils do
  def timestamp_to_prefix(timestamp, camera_name) do
    datetime = DateTime.from_unix!(timestamp, :second)
    # Pad single-digit values with leading zeros to match your directory structure
    year = to_string(datetime.year)
    month = pad_zero(datetime.month)
    day = pad_zero(datetime.day)
    hour = pad_zero(datetime.hour)

    "#{camera_name}/recordings/#{year}/#{month}/#{day}/#{hour}/"
  end

  defp pad_zero(num) when num < 10, do: "0#{num}"
  defp pad_zero(num), do: to_string(num)
end

For time ranges spanning multiple days/hours, you can generate all intermediate prefixes, but we'll cover a smarter way using your existing snapshot data next.

Step 2: Fetch All File Keys to Delete

Since you already have a function that returns snapshot details for a time range, you can reuse this to extract the full file paths (keys) of the JPEGs you need to delete. Assuming your snapshot objects include a file_path field (the full SeaweedFS key):

def get_deletion_keys(start_ts, end_ts, camera_name) do
  # Call your existing snapshot fetching function
  %{"snapshots" => snapshots} = YourApp.Snapshots.get_snapshots(start_ts, end_ts, camera_name)
  # Extract just the file paths from each snapshot
  Enum.map(snapshots, fn snapshot -> snapshot["file_path"] end)
end

If you prefer to query SeaweedFS directly (instead of reusing the snapshot function), you can call SeaweedFS's List API with your prefixes to fetch matching keys. Using Tesla as the HTTP client:

def list_keys_by_prefix(prefix, collection) do
  url = "http://your_seaweedfs_master:9333/dir/list?collection=#{collection}&prefix=#{prefix}"
  case Tesla.get(url) do
    {:ok, %Tesla.Env{status: 200, body: body}} ->
      Jason.decode!(body) |> Map.get("entries", []) |> Enum.map(& &1["fullPath"])
    {:error, reason} ->
      {:error, reason}
  end
end

Step 3: Implement SeaweedFS Deletion Logic

SeaweedFS provides a simple HTTP API to delete files by their key. Let's wrap this in a reusable client module:

defmodule YourApp.SeaweedFS.Client do
  @seaweedfs_master "http://your_seaweedfs_master:9333"
  @default_collection "your_collection_name" # Replace with your actual collection

  def delete_file(key, collection \\ @default_collection) do
    url = "#{@seaweedfs_master}/dir/delete?collection=#{collection}&key=#{key}"
    case Tesla.delete(url) do
      {:ok, %Tesla.Env{status: 200}} -> :ok
      {:ok, %Tesla.Env{status: status}} -> {:error, "Delete failed with status #{status}"}
      {:error, reason} -> {:error, inspect(reason)}
    end
  end

  # Batch delete with parallel processing for better performance
  def batch_delete(keys, collection \\ @default_collection) do
    keys
    |> Task.async_stream(fn key -> {key, delete_file(key, collection)} end, max_concurrency: 10)
    |> Enum.map(fn {:ok, result} -> result end)
  end
end

Using Task.async_stream lets you delete files in parallel, which is much faster than deleting one-by-one for large batches.

Step 4: Build the Phoenix Deletion Endpoint

Now, add a controller action to handle the deletion request, and wire it up in your router.

Controller Action

defmodule YourAppWeb.CameraSnapshotController do
  use YourAppWeb, :controller
  alias YourApp.SeaweedFS.Client
  alias YourApp.Snapshots

  action_fallback YourAppWeb.FallbackController # For centralized error handling

  def delete_snapshots(conn, %{"camera_name" => camera_name, "start_ts" => start_ts, "end_ts" => end_ts}) do
    with start_ts <- String.to_integer(start_ts),
         end_ts <- String.to_integer(end_ts),
         keys <- Snapshots.get_deletion_keys(start_ts, end_ts, camera_name),
         results <- Client.batch_delete(keys) do
      # Calculate success/failure counts
      {success, failures} = Enum.split_with(results, fn {_key, res} -> res == :ok end)

      conn
      |> put_status(:ok)
      |> json(%{
        message: "Deletion process completed",
        deleted_count: length(success),
        failed_count: length(failures),
        failed_files: Enum.map(failures, fn {key, err} -> %{file_key: key, error: err} end)
      })
    end
  end
end

Router Configuration

Add this route to your lib/your_app_web/router.ex:

scope "/api", YourAppWeb do
  pipe_through :api
  delete "/cameras/:camera_name/snapshots", CameraSnapshotController, :delete_snapshots
end

Key Considerations

  • Authentication & Authorization: Don't forget to add auth checks (like Guardian or Pow) to this endpoint to prevent unauthorized deletions. Add a plug to your controller:
    plug Guardian.Plug.EnsureAuthenticated, handler: YourAppWeb.AuthErrorHandler when action in [:delete_snapshots]
    
  • Error Handling: Use Phoenix's fallback controller to handle cases like invalid timestamps, SeaweedFS connection issues, etc.
  • Prefix Deletion Shortcut: If your SeaweedFS version supports it, you can delete all files under a prefix in one request with DELETE /dir/delete?collection=your_collection&prefix=your_prefix. This is faster than deleting individual files, but verify your version supports this feature first.
  • Idempotency: Deleting a file that's already removed will return a 200, so retries are safe if needed.

内容的提问来源于stack exchange,提问作者Junaid Farooq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:53:04