mirror of
https://github.com/filebrowser/filebrowser.git
synced 2026-01-23 02:35:10 +00:00
feat: support streaming response for search results (#5630)
Co-authored-by: manx98 <1323517022@qq.com>
This commit is contained in:
parent
f89975603e
commit
20bfd131c6
10 changed files with 183 additions and 39 deletions
|
|
@ -1,28 +1,82 @@
|
|||
package fbhttp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/filebrowser/filebrowser/v2/search"
|
||||
)
|
||||
|
||||
const searchPingInterval = 5
|
||||
|
||||
var searchHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
response := []map[string]interface{}{}
|
||||
response := make(chan map[string]interface{})
|
||||
ctx, cancel := context.WithCancelCause(r.Context())
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Avoid connection timeout
|
||||
timeout := time.NewTimer(searchPingInterval * time.Second)
|
||||
defer timeout.Stop()
|
||||
for {
|
||||
var err error
|
||||
var infoBytes []byte
|
||||
select {
|
||||
case info := <-response:
|
||||
if info == nil {
|
||||
return
|
||||
}
|
||||
infoBytes, err = json.Marshal(info)
|
||||
case <-timeout.C:
|
||||
// Send a heartbeat packet
|
||||
infoBytes = nil
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
return
|
||||
}
|
||||
_, err = w.Write(infoBytes)
|
||||
if err == nil {
|
||||
_, err = w.Write([]byte("\n"))
|
||||
}
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
return
|
||||
}
|
||||
if flusher, ok := w.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
}()
|
||||
query := r.URL.Query().Get("query")
|
||||
|
||||
err := search.Search(d.user.Fs, r.URL.Path, query, d, func(path string, f os.FileInfo) error {
|
||||
response = append(response, map[string]interface{}{
|
||||
err := search.Search(ctx, d.user.Fs, r.URL.Path, query, d, func(path string, f os.FileInfo) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case response <- map[string]interface{}{
|
||||
"dir": f.IsDir(),
|
||||
"path": path,
|
||||
})
|
||||
|
||||
return nil
|
||||
}:
|
||||
}
|
||||
return context.Cause(ctx)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
close(response)
|
||||
wg.Wait()
|
||||
if err == nil {
|
||||
err = context.Cause(ctx)
|
||||
}
|
||||
// ignore cancellation errors from user aborts
|
||||
if err != nil && !errors.Is(err, context.Canceled) {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
return renderJSON(w, r, response)
|
||||
return 0, nil
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue