From b52c9fe30564e209398f8f2eb5c4d7beb0558099 Mon Sep 17 00:00:00 2001 From: Xu Han Date: Sat, 7 Oct 2023 13:36:43 +0800 Subject: [PATCH] serve local data under deploying origin (#6) --- app/action/start.go | 2 +- app/backend/segmentation.go | 6 ++-- .../panel/layer/mask/Segment/FocusCanvas.tsx | 4 +-- .../panel/layer/mask/Segment/WholeCanvas.tsx | 4 +-- .../panel/layer/mask/Segment/common.ts | 13 +++++++++ app/frontend/src/state/image/store.ts | 9 +----- docs/docs/03-Usage/02-Resource.mdx | 28 +++++++++++-------- main.go | 2 +- 8 files changed, 39 insertions(+), 29 deletions(-) diff --git a/app/action/start.go b/app/action/start.go index 4aba6ca..55c751a 100644 --- a/app/action/start.go +++ b/app/action/start.go @@ -69,7 +69,7 @@ func Start(ctx context.Context) error { // local data if StartOption.DataDir != "" { zap.L().Info("serving local data", zap.String("dir", StartOption.DataDir)) - e.Static("/local", StartOption.DataDir) + e.Static("/data", StartOption.DataDir) } // backend diff --git a/app/backend/segmentation.go b/app/backend/segmentation.go index bfc7628..a22665a 100644 --- a/app/backend/segmentation.go +++ b/app/backend/segmentation.go @@ -206,10 +206,10 @@ func (s *mServer) onlineSegmentationEmbed(ctx context.Context, request nutshapi. } func (s *mServer) loadImage(ctx context.Context, url string) ([]byte, error) { - localPrefix := "file://" - if strings.HasPrefix(url, localPrefix) { + dataPrefix := "data://" + if strings.HasPrefix(url, dataPrefix) { // the image should be loaded from data dir - relPath := strings.TrimPrefix(url, localPrefix) + relPath := strings.TrimPrefix(url, dataPrefix) dir := s.options.dataDir if dir == "" { return nil, errors.Errorf("missing data dir to load local image [%s]", relPath) diff --git a/app/frontend/src/component/panel/layer/mask/Segment/FocusCanvas.tsx b/app/frontend/src/component/panel/layer/mask/Segment/FocusCanvas.tsx index 1c69069..364f1ee 100644 --- a/app/frontend/src/component/panel/layer/mask/Segment/FocusCanvas.tsx +++ b/app/frontend/src/component/panel/layer/mask/Segment/FocusCanvas.tsx @@ -17,7 +17,7 @@ import {NutshClientContext} from 'common/context'; import {useGetOnlineSegmentationEmbedding} from 'state/server/segmentation'; import {SizedContainer} from 'component/SizedContainer'; -import {Decoder, downloadTensor} from './common'; +import {Decoder, downloadTensor, correctSliceUrl} from './common'; import {LocalEmbedding, PredictContainer} from './PredictContainer'; import {ColorPalette} from 'component/panel/entity/display'; import {SegmentationSample} from 'proto/schema/v1/train_pb'; @@ -200,7 +200,7 @@ const Workspace: FC & MaskProps & {cropImage: Rec const client = useContext(NutshClientContext); const sliceUrl = useRenderStore(s => s.sliceUrls[s.sliceIndex]); const {isFetching: isEmbedding, data: embedResp} = useGetOnlineSegmentationEmbedding(client, { - imageUrl: sliceUrl, + imageUrl: correctSliceUrl(sliceUrl), decoderUuid: decoder.uuid, crop: cropImage, }); diff --git a/app/frontend/src/component/panel/layer/mask/Segment/WholeCanvas.tsx b/app/frontend/src/component/panel/layer/mask/Segment/WholeCanvas.tsx index ae99b9d..9428cf5 100644 --- a/app/frontend/src/component/panel/layer/mask/Segment/WholeCanvas.tsx +++ b/app/frontend/src/component/panel/layer/mask/Segment/WholeCanvas.tsx @@ -13,7 +13,7 @@ import {useStore as useUIStore} from 'state/annotate/ui'; import {useStore as useRenderStore} from 'state/annotate/render'; import {useImageContext, useMaskedImageContext, useUpdateMask} from '../common'; -import {Decoder, downloadTensor} from './common'; +import {Decoder, downloadTensor, correctSliceUrl} from './common'; import {PredictContainer} from './PredictContainer'; import {SegmentationSample} from 'proto/schema/v1/train_pb'; import {emitter} from 'event'; @@ -26,7 +26,7 @@ export const WholeCanvas: FC = ({decoder, ...divP const client = useContext(NutshClientContext); const sliceUrl = useRenderStore(s => s.sliceUrls[s.sliceIndex]); const {isFetching, data} = useGetOnlineSegmentationEmbedding(client, { - imageUrl: sliceUrl, + imageUrl: correctSliceUrl(sliceUrl), decoderUuid: decoder.uuid, }); if (!data) { diff --git a/app/frontend/src/component/panel/layer/mask/Segment/common.ts b/app/frontend/src/component/panel/layer/mask/Segment/common.ts index 481ddbd..b1799e5 100644 --- a/app/frontend/src/component/panel/layer/mask/Segment/common.ts +++ b/app/frontend/src/component/panel/layer/mask/Segment/common.ts @@ -15,3 +15,16 @@ export function downloadTensor(url: string): Promise { }); }); } + +// The server has no idea of under what host name it is serving, which only the client knows. +// In particular, a url to a local data may not be downloadable by the server. +// Therefore, the client needs to correct a local-data url to something the server is aware of. +export function correctSliceUrl(url: string): string { + const {origin} = window.location; + const dataPrefix = `${origin}/data/`; + if (url.startsWith(dataPrefix)) { + const rel = url.substring(dataPrefix.length); + return `data://${rel}`; + } + return url; +} diff --git a/app/frontend/src/state/image/store.ts b/app/frontend/src/state/image/store.ts index 686f1be..404ebeb 100644 --- a/app/frontend/src/state/image/store.ts +++ b/app/frontend/src/state/image/store.ts @@ -4,14 +4,7 @@ import {useQuery, QueryClient} from '@tanstack/react-query'; const client = new QueryClient(); const context = createContext(client); -const query = (u: string) => { - let url = u; - - const localPrefix = 'file://'; - if (u.startsWith(localPrefix)) { - url = `/local/${u.substring(localPrefix.length)}`; - } - +const query = (url: string) => { return { queryKey: ['downloadImage', url], queryFn: async () => await (await fetch(url)).blob(), diff --git a/docs/docs/03-Usage/02-Resource.mdx b/docs/docs/03-Usage/02-Resource.mdx index 77ec8f5..817c603 100644 --- a/docs/docs/03-Usage/02-Resource.mdx +++ b/docs/docs/03-Usage/02-Resource.mdx @@ -10,14 +10,16 @@ Through the user interface, you can effortlessly create, search for, update, and ## Loading Local Files -Resource assets are specified by their URLs, e.g. a video is represented by a list of urls of its frames. -By default, the URL should use the HTTP protocol, which isn't convenient for directly loading local files. -You can take the following steps to work with local files. +Resource assets are specified by their URLs. For example, a video is represented by a list of URLs for its frames. +Usually these URLs use the HTTP protocol, which is not convenient for directly loading local files. +To work with local files, follow the steps below: -1. Start nutsh with an additional flag `--data-dir [data_dir]` pointing to some directory where your data is located. -2. Use `file://[rel_path]` as the URL for your resources, which will point to the file at `[data_dir]/[rel_path]` on your local machine. +1. Start nutsh with an additional flag `--data-dir ` pointing to some directory where your data is located. +2. Identify the [origin](https://web.dev/same-site-same-origin/#origin) of the nutsh website you are visiting, which typically shoud be of the format `http(s)://(:port)`. +3. Use `/data/` for the file located at `/` on your local machine. + - Pay attention to the `data` prefix in the URL before the relative path to your file. -For instance, if you're working with videos stored in +For instance, if you have videos stored in: ``` /var/data/abc @@ -32,22 +34,24 @@ For instance, if you're working with videos stored in - ... ``` -Start nutsh with the flag `--data-dir /var/data/abc`, and create your videos with urls like +Start nutsh with the flag `--data-dir /var/data/abc`. + +Then, if somehow you visit your deployed nutsh at `https://nutsh.my-instutite.com`, use the following URLs for your data: ``` -file://video0001/frame0001.jpg -file://video0001/frame0002.jpg +https://nutsh.my-instutite.com/data/video0001/frame0001.jpg +https://nutsh.my-instutite.com/data/video0001/frame0002.jpg ... ``` -:::caution +:::tip -Always use relative paths. Namely, a local url should never start with `file:///` but only two slashes `file://`. +You can always enter the URL into the browser's address bar to check if it works. ::: :::danger -You may use `--data-dir /` to serve files with absolute paths, like `file://var/data/abc/video0001/frame0001.jpg`, but do note that it exposes security risks for your host machine. +While you can use `--data-dir /` to serve essentially any file, be aware that this exposes significant security risks for your host machine. ::: diff --git a/main.go b/main.go index 5961d11..865240c 100644 --- a/main.go +++ b/main.go @@ -64,7 +64,7 @@ func main() { }, &cli.StringFlag{ Name: "data-dir", - Usage: "data directory to serve local files under `file://`", + Usage: "data directory to serve local files", EnvVars: []string{"NUTSH_DATA_DIR"}, Destination: &action.StartOption.DataDir, },