--- Assets upload + raw serving --- ImportAsset writes raw bytes into the vault root and returns the markdown snippet to embed relative to the source note.
(notePath, filename string, body io.Reader)
| 2088 | // ImportAsset writes raw bytes into the vault root and returns the |
| 2089 | // markdown snippet to embed relative to the source note. |
| 2090 | func (v *Vault) ImportAsset(notePath, filename string, body io.Reader) (ImportedAsset, error) { |
| 2091 | v.mu.Lock() |
| 2092 | defer v.mu.Unlock() |
| 2093 | if err := os.MkdirAll(v.root, v.dirMode); err != nil { |
| 2094 | return ImportedAsset{}, err |
| 2095 | } |
| 2096 | safeName := sanitizeFileName(filename) |
| 2097 | if safeName == "" { |
| 2098 | safeName = "file" |
| 2099 | } |
| 2100 | ext := filepath.Ext(safeName) |
| 2101 | stem := strings.TrimSuffix(safeName, ext) |
| 2102 | abs := uniquePath(v.root, stem, ext) |
| 2103 | f, err := os.OpenFile(abs, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, v.fileMode) |
| 2104 | if err != nil { |
| 2105 | return ImportedAsset{}, err |
| 2106 | } |
| 2107 | cleanupPartial := func() { |
| 2108 | _ = f.Close() |
| 2109 | _ = os.Remove(abs) |
| 2110 | } |
| 2111 | limited := io.LimitReader(body, v.maxAssetBytes+1) |
| 2112 | written, err := io.Copy(f, limited) |
| 2113 | if err != nil { |
| 2114 | cleanupPartial() |
| 2115 | return ImportedAsset{}, err |
| 2116 | } |
| 2117 | if written > v.maxAssetBytes { |
| 2118 | cleanupPartial() |
| 2119 | return ImportedAsset{}, ErrAssetTooLarge |
| 2120 | } |
| 2121 | if err := f.Close(); err != nil { |
| 2122 | _ = os.Remove(abs) |
| 2123 | return ImportedAsset{}, err |
| 2124 | } |
| 2125 | rel := filepath.ToSlash(filepath.Base(abs)) |
| 2126 | noteDir := filepath.Dir(filepath.FromSlash(notePath)) |
| 2127 | if noteDir == "." { |
| 2128 | noteDir = "" |
| 2129 | } |
| 2130 | markdownPath := rel |
| 2131 | if noteDir != "" { |
| 2132 | if relative, err := filepath.Rel(noteDir, rel); err == nil { |
| 2133 | markdownPath = filepath.ToSlash(relative) |
| 2134 | } |
| 2135 | } |
| 2136 | kind := kindForExt(strings.ToLower(filepath.Ext(abs))) |
| 2137 | markdown := makeAssetMarkdown(markdownPath, kind, filepath.Base(abs)) |
| 2138 | return ImportedAsset{ |
| 2139 | Name: filepath.Base(abs), |
| 2140 | Path: rel, |
| 2141 | Markdown: markdown, |
| 2142 | Kind: kind, |
| 2143 | }, nil |
| 2144 | } |
| 2145 | |
| 2146 | func (v *Vault) AssetAbsPath(rel string) (string, error) { |
| 2147 | v.mu.RLock() |