Packages
mongodb_driver
0.7.2
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.6
1.5.5
1.5.4
1.5.2
1.5.1
1.5.0
1.4.1
1.4.0
1.2.1
1.2.0
1.1.0
1.0.3
1.0.2
1.0.1
1.0.0
0.9.2
0.9.1
0.9.0
0.9.0-rc.1
0.9.0-rc.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
The MongoDB driver for Elixir
Current section
Files
Jump to
Current section
Files
lib/mongo/grid_fs/download.ex
defmodule Mongo.GridFs.Download do
@moduledoc """
The main entry point for downloading files from the grid-fs specified by the bucket struct.
"""
alias BSON.ObjectId
alias Mongo.GridFs.Bucket
@type result :: {:error, :unknown} | {:error, :length_is_zero} | {:error, :not_found} | {:ok, Mongo.cursor}
@doc """
Opens a Stream from which the application can read the contents of the stored file
specified by fileId. The fileId can be a string, an ObjectId or just a map with the
keys `length` and `_id`. In case of the map the function tries to stream the chunks
described by the `length` and the `_id` values.
Returns a Stream.
"""
@spec open_download_stream(Bucket.t, String.t | BSON.ObjectId.t | map()) :: result
def open_download_stream(%Bucket{topology_pid: topology_pid, opts: opts} = bucket, file_id) when is_binary(file_id) do
topology_pid
|> Mongo.find_one(Bucket.files_collection_name(bucket), %{"_id" => ObjectId.decode!(file_id)}, opts)
|> stream_chunk(bucket)
end
def open_download_stream(%Bucket{topology_pid: topology_pid, opts: opts} = bucket, %BSON.ObjectId{} = oid) do
topology_pid
|> Mongo.find_one(Bucket.files_collection_name(bucket), %{"_id" => oid}, opts)
|> stream_chunk(bucket)
end
def open_download_stream(bucket, %{"length" => _, "_id" => _} = file) do
stream_chunk(file, bucket)
end
@doc """
Same as above, but returns also the file document.
"""
@spec find_and_stream(Bucket.t, String.t) :: {result, BSON.document}
def find_and_stream(bucket, file_id)
def find_and_stream(%Bucket{topology_pid: topology_pid, opts: opts} = bucket, file_id) when is_binary(file_id) do
file = Mongo.find_one(topology_pid, Bucket.files_collection_name(bucket), %{"_id" => ObjectId.decode!(file_id)}, opts)
{stream_chunk(file, bucket), file}
end
def find_and_stream(%Bucket{topology_pid: topology_pid, opts: opts} = bucket, file_id) do
file = Mongo.find_one(topology_pid, Bucket.files_collection_name(bucket), %{"_id" => file_id}, opts)
{stream_chunk(file, bucket), file}
end
def find_one_file(%Bucket{topology_pid: topology_pid, opts: opts} = bucket, filename) when is_binary(filename) do
Mongo.find_one(topology_pid, Bucket.files_collection_name(bucket), %{"filename" => filename}, opts)
end
def find_one_file(%Bucket{topology_pid: topology_pid, opts: opts} = bucket, %BSON.ObjectId{} = file_id) do
Mongo.find_one(topology_pid, Bucket.files_collection_name(bucket), %{"_id" => file_id}, opts)
end
##
# In case that the file map is nil we return :error
#
defp stream_chunk(nil, _bucket), do: {:error, :not_found}
##
# However, when downloading a zero length stored file the driver MUST NOT issue a query against the chunks
# collection, since that query is not necessary. For a zero length file, drivers return either an empty
# stream or send nothing to the provided stream (depending on the download method).
##
defp stream_chunk(%{"length" => 0}, _bucket), do: {:error, :length_is_zero}
##
# Streaming the chunks with `file_id` sorted ascending by n
#
defp stream_chunk(%{"_id" => id}, %Bucket{topology_pid: topology_pid, opts: opts} = bucket) do
opts = Keyword.merge(opts, [sort: [n: 1]])
stream = topology_pid
|> Mongo.find(Bucket.chunks_collection_name(bucket), %{files_id: id}, opts)
|> Stream.map(fn map -> map["data"].binary end)
{:ok, stream}
end
##
# catch up for other cases
#
defp stream_chunk(_, _bucket), do: {:error, :unknown}
end