Skip to content

Commit

Permalink
serve local data under deploying origin (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
hxhxhx88 authored Oct 7, 2023
1 parent dab7955 commit b52c9fe
Show file tree
Hide file tree
Showing 8 changed files with 39 additions and 29 deletions.
2 changes: 1 addition & 1 deletion app/action/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions app/backend/segmentation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -200,7 +200,7 @@ const Workspace: FC<HTMLAttributes<HTMLDivElement> & 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,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -26,7 +26,7 @@ export const WholeCanvas: FC<DivProps & {decoder: Decoder}> = ({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) {
Expand Down
13 changes: 13 additions & 0 deletions app/frontend/src/component/panel/layer/mask/Segment/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,16 @@ export function downloadTensor(url: string): Promise<Tensor> {
});
});
}

// 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;
}
9 changes: 1 addition & 8 deletions app/frontend/src/state/image/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,7 @@ import {useQuery, QueryClient} from '@tanstack/react-query';
const client = new QueryClient();
const context = createContext<QueryClient | undefined>(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(),
Expand Down
28 changes: 16 additions & 12 deletions docs/docs/03-Usage/02-Resource.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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 <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)://<host>(:port)`.
3. Use `<nutsh_origin>/data/<rel_path>` for the file located at `<data_dir>/<rel_path>` 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
Expand All @@ -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.

:::
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down

0 comments on commit b52c9fe

Please sign in to comment.