1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
use crate::{Blob, File};
use std::{ops::Deref, rc::Rc};

use wasm_bindgen::UnwrapThrowExt;
use web_sys::Url;

struct ObjectUrlAllocation {
    url: String,
}

impl Drop for ObjectUrlAllocation {
    fn drop(&mut self) {
        web_sys::Url::revoke_object_url(&self.url).unwrap_throw();
    }
}

/// A resource wrapper around [`URL.createObjectURL`] / [`URL.revokeObjectURL`].
///
/// A [`Blob`], in particular a [`File`], can be converted to a short URL representing its data with the above methods.
/// An [`ObjectUrl`] can be cheaply cloned and shared and revokes the underlying URL when the last reference is dropped.
///
/// Note that multiple urls can be created for the same blob, without being guaranteed to be de-deduplicated.
///
/// # Example
///
/// ```rust,no_run
/// use gloo_file::{Blob, ObjectUrl};
///
/// let blob = Blob::new("hello world");
/// let object_url = ObjectUrl::from(blob);
/// ```
///
/// [`URL.createObjectURL`]: https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
/// [`URL.revokeObjectURL`]: https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL
/// [`File`]: crate::File
#[derive(Clone)]
pub struct ObjectUrl {
    inner: Rc<ObjectUrlAllocation>,
}

impl From<File> for ObjectUrl {
    fn from(file: File) -> Self {
        Blob::from(file).into()
    }
}

impl From<Blob> for ObjectUrl {
    fn from(blob: Blob) -> Self {
        web_sys::Blob::from(blob).into()
    }
}

impl From<web_sys::Blob> for ObjectUrl {
    fn from(blob: web_sys::Blob) -> Self {
        let url = Url::create_object_url_with_blob(&blob).unwrap_throw();
        let inner = Rc::new(ObjectUrlAllocation { url });
        Self { inner }
    }
}

// Note: some browsers support Url::create_object_url_with_source but this is deprecated!
// https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL#using_object_urls_for_media_streams

impl Deref for ObjectUrl {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        &self.inner.url
    }
}