|
diff --git a/hfiles.go b/hfiles.go
|
| ... |
| 10 |
"net/http" |
10 |
"net/http" |
| 11 |
"path" |
11 |
"path" |
| 12 |
"sort" |
12 |
"sort" |
|
|
13 |
"strconv" |
| 13 |
"strings" |
14 |
"strings" |
| 14 |
|
15 |
|
| 15 |
"github.com/go-git/go-git/v5/plumbing" |
16 |
"github.com/go-git/go-git/v5/plumbing" |
| ... |
| 115 |
return |
116 |
return |
| 116 |
} |
117 |
} |
| 117 |
|
118 |
|
|
|
119 |
isBinary, _ := file.IsBinary() |
|
|
120 |
if isBinary { |
|
|
121 |
http.Redirect(w, r, fmt.Sprintf("/r/%s/raw/%s?ref=%s", ctx.Repo.Name, pathValue, ctx.CurrentRef), http.StatusFound) |
|
|
122 |
return |
|
|
123 |
} |
|
|
124 |
|
| 118 |
content, err := file.Contents() |
125 |
content, err := file.Contents() |
| 119 |
if err != nil { |
126 |
if err != nil { |
| 120 |
http.Error(w, fmt.Sprintf("Error reading file: %v", err), http.StatusInternalServerError) |
127 |
http.Error(w, fmt.Sprintf("Error reading file: %v", err), http.StatusInternalServerError) |
| ... |
| 164 |
} |
171 |
} |
| 165 |
defer reader.Close() |
172 |
defer reader.Close() |
| 166 |
|
173 |
|
| 167 |
w.Header().Set("Content-Type", "application/octet-stream") |
174 |
// Read first 512 bytes to detect content type |
| 168 |
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", pathValue)) |
175 |
data := make([]byte, 512) |
|
|
176 |
n, err := io.ReadFull(reader, data) |
|
|
177 |
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { |
|
|
178 |
http.Error(w, fmt.Sprintf("Error reading file: %v", err), http.StatusInternalServerError) |
|
|
179 |
return |
|
|
180 |
} |
|
|
181 |
data = data[:n] |
|
|
182 |
|
|
|
183 |
contentType := http.DetectContentType(data) |
|
|
184 |
|
|
|
185 |
// Fallback for some common types if DetectContentType returns octet-stream or text/plain |
|
|
186 |
if contentType == "application/octet-stream" || contentType == "text/plain; charset=utf-8" { |
|
|
187 |
ext := strings.ToLower(path.Ext(pathValue)) |
|
|
188 |
switch ext { |
|
|
189 |
case ".go", ".ts", ".js", ".md", ".txt", ".json", ".yaml", ".yml", ".sh", ".c", ".h", ".cpp", ".hpp", ".css", ".html": |
|
|
190 |
contentType = "text/plain; charset=utf-8" |
|
|
191 |
case ".pdf": |
|
|
192 |
contentType = "application/pdf" |
|
|
193 |
case ".svg": |
|
|
194 |
contentType = "image/svg+xml" |
|
|
195 |
} |
|
|
196 |
} |
|
|
197 |
|
|
|
198 |
w.Header().Set("Content-Type", contentType) |
|
|
199 |
w.Header().Set("Content-Length", strconv.FormatInt(file.Size, 10)) |
|
|
200 |
|
|
|
201 |
// Decide if it should be inline or attachment |
|
|
202 |
disposition := "inline" |
|
|
203 |
if contentType == "application/octet-stream" { |
|
|
204 |
disposition = "attachment" |
|
|
205 |
} |
|
|
206 |
|
|
|
207 |
w.Header().Set("Content-Disposition", fmt.Sprintf("%s; filename=\"%s\"", disposition, path.Base(pathValue))) |
|
|
208 |
|
|
|
209 |
// Write the first bytes we read |
|
|
210 |
w.Write(data) |
|
|
211 |
// Then copy the rest |
| 169 |
io.Copy(w, reader) |
212 |
io.Copy(w, reader) |
| 170 |
} |
213 |
} |
| 171 |
|
214 |
|
| ... |