# How to load nifti file from web browser link?

**URL:** <https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664>\
**Category:** Development\
**Created:** [July 13, 2021, 12:44pm UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664 "2021-07-13T12:44:28Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![ASH](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/ash/32/11599_2.png) [@ASH](https://discourse.slicer.org/u/ASH)\
**Post date:** [July 13, 2021, 12:44pm UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/1 "2021-07-13T12:44:28Z")

</div>

Hi there,  
I have a nifti file (xxx.nii.gz) and can open it in 3D slicer with drag and drop the file to the app.  
I watched the video ([Using 3D Slicer with cloud DICOMweb databases - YouTube](https://www.youtube.com/watch?v=60_hzSlptWY)) and found a DICOM can be opened from web browser lnk. Now, I want to load a nifti file in 3D slicer just like that. Is it possible?

I noticed a link looks like `slicer://viewer?studyUID=xxx&access_token=xxx&dicomweb_endpoint=httpsxxx&dicomweb_uri_endpoint=httpsxxx`, but couldn’t find a document what value should be passed for each argment.

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [July 13, 2021, 4:14pm UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/2 "2021-07-13T16:14:01Z")

</div>

This is easily doable. All you need to do is to create a small Python scripted module that handles this URL. You can generate the skeleton of the module using Extension Wizard and then you would only keep the module class that would set up the callback function [as it is done in the DICOM module](https://github.com/Slicer/Slicer/blob/master/Modules/Scripted/DICOM/DICOM.py#L52).

Your callback function could be something like this (you can copy-paste this code into the Python console to test it):

```python
def reportProgress(message, level, sampleDataLogic):
    # Print progress in the console
    print("Loading... {0}%".format(sampleDataLogic.downloadPercent))
    # Abort download if cancel is clicked in progress bar
    progressWindow = sampleDataLogic.progressWindow
    if progressWindow.wasCanceled:
        raise Exception("download aborted")
    # Update progress window
    progressWindow.show()
    progressWindow.activateWindow()
    progressWindow.setValue(int(sampleDataLogic.downloadPercent))
    progressWindow.setLabelText("Downloading...")
    # Process events to allow screen to refresh
    slicer.app.processEvents()

def onURLReceived(urlString):
    """Process DICOM view requests. Example:
    urlString="slicer://viewer/?download=https%3A%2F%2Fgithub.com%2Frbumm%2FSlicerLungCTAnalyzer%2Freleases%2Fdownload%2FSampleData%2FLungCTAnalyzerChestCT.nrrd"
    """
    url = qt.QUrl(urlString)
    if url.authority().lower() != "viewer":
        return
    query = qt.QUrlQuery(url)
    queryMap = {}
    for key, value in query.queryItems(qt.QUrl.FullyDecoded):
        queryMap[key] = qt.QUrl.fromPercentEncoding(value)
    if not "download" in queryMap:
        return
    downloadUrl = qt.QUrl(queryMap["download"])
    nodeName, ext = os.path.splitext(os.path.basename(downloadUrl.path()))
    import uuid
    # Generate random filename to avoid reusing/overwriting older downloaded files that may have the same name
    filename = f"{nodeName}-{uuid.uuid4().hex}{ext}"
    print(filename)
    try:
        progressWindow = slicer.util.createProgressDialog()
        import SampleData
        sampleDataLogic = SampleData.SampleDataLogic()
        sampleDataLogic.progressWindow = progressWindow
        sampleDataLogic.logMessage = lambda message, level=None, sampleDataLogic=sampleDataLogic: reportProgress(message, level, sampleDataLogic)
        loadedNodes = sampleDataLogic.downloadFromURL(nodeNames=nodeName, fileNames=filename, uris=downloadUrl.toString())
    finally:
        progressWindow.close()

```

To test, run this in the Python console (to download this sample nrrd file: [https://github.com/rbumm/SlicerLungCTAnalyzer/releases/download/SampleData/LungCTAnalyzerChestCT.nrrd](https://github.com/rbumm/SlicerLungCTAnalyzer/releases/download/SampleData/LungCTAnalyzerChestCT.nrrd))

```python
onURLReceived("slicer://viewer/?download=https%3A%2F%2Fgithub.com%2Frbumm%2FSlicerLungCTAnalyzer%2Freleases%2Fdownload%2FSampleData%2FLungCTAnalyzerChestCT.nrrd")

```

You can of course tune the URL format, add checksum (SampleData module can automatically cache downloaded data sets to avoid repeated downloads if the checksum value matches), etc.

---

<div class="post-metadata">

**Author:** ![ASH](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/ash/32/11599_2.png) [@ASH](https://discourse.slicer.org/u/ASH)\
**Post date:** [July 14, 2021, 3:19am UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/3 "2021-07-14T03:19:14Z")

</div>

Thanks for the reply. Some parts are not clear to me as I’m new to 3D slicer and python. Appreaciate if you could help me bit more.

Firstly, I followed “Creating Extensions” section in the below page to create an extension/module named “loadRemoteFile”:  
[https://www.slicer.org/wiki/Documentation/Nightly/Developers/ExtensionWizard](https://www.slicer.org/wiki/Documentation/Nightly/Developers/ExtensionWizard)

I added `slicer.app.connect("urlReceived(QString)", self.onURLReceived)` line and the script you mentioned above in loadRemoteFile.py. Then loaded the module from “Module finder” (It should be loaded automatically anyway as I checked ‘Add selected module to search paths’ when creaed extension). No error occurs at this point. However, if I run "onURLReceived(“slicer…” in Python interactor, get an error “NameError: name ‘onURLReceived’ is not defined”. What am I missing?

 ![Annotate-a-local-image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/a/8/a8734e2d260f2a3c1da750d6cfe69e5020d690b1.png)

Also, I would like to know the way to pass the module I created to somebody. Give the whole contents of loadRemoteFile folder then users load it from Extension wizard-\>Select extension?

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [July 14, 2021, 3:34am UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/4 "2021-07-14T03:34:03Z")

</div>

`onURLReceived` function is not found in the Python console if you don’t copy it into the Python console.

To test your module, you can launch `Slicer.exe` with the URL as the only command-line argument:

```
Slicer.exe slicer://viewer/?download=https%3A%2F%2Fgithub.com%2Frbumm%2FSlicerLungCTAnalyzer%2Freleases%2Fdownload%2FSampleData%2FLungCTAnalyzerChestCT.nrrd

```

To test if the custom URL association is set up correctly, you can open a command terminal in Windows and execute this:

```
start slicer://viewer/?download=https%3A%2F%2Fgithub.com%2Frbumm%2FSlicerLungCTAnalyzer%2Freleases%2Fdownload%2FSampleData%2FLungCTAnalyzerChestCT.nrrd`

```

> [@ASH](#):
>
> Also, I would like to know the way to pass the module I created to somebody.

Probably the easiest is to copy your module files to the `lib\Slicer-4.13\qt-scripted-modules` folder in the Slicer installation.

---

<div class="post-metadata">

**Author:** ![ASH](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/ash/32/11599_2.png) [@ASH](https://discourse.slicer.org/u/ASH)\
**Post date:** [July 14, 2021, 6:01am UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/5 "2021-07-14T06:01:41Z")

</div>

mmm… I’m still struggling. 3D slicer won’t complain anything if open up normally, however open up with “Slicer.exe slicer://…” or “start slicer://…” gets “Missing dicomweb\_endpoint” error. Sounds like the module is not loaded correctly.

```auto
DICOM module received URL: slicer://viewer/?download=https%3A%2F%2Fgithub.com%2Frbumm%2FSlicerLungCTAnalyzer%2Freleases%2Fdownload%2FSampleData%2FLungCTAnalyzerChestCT.nrrd
Missing dicomweb_endpoint

```

The app complains if I insert something meaningless text in loadRemoteFile.py intentionally, so apparently the app is reading the py file.  
I share the py file below. Could you check if anything wrong please?  
I commented out `slicer.app.connect("startupCompleted()", registerSampleData)` at line 35.  
“slicer.app.connect” is in line 36 followed by reportProgress and onURLReceived functions.

> **[loadRemoteFile.py](https://drive.google.com/file/d/1j6WRLAt7vdaa4kzYYyyFG3XBPUa_73ZG/view)**
>
> Google Drive file.

---

<div class="post-metadata">

**Author:** ![ASH](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/ash/32/11599_2.png) [@ASH](https://discourse.slicer.org/u/ASH)\
**Post date:** [July 14, 2021, 12:39pm UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/6 "2021-07-14T12:39:18Z")

</div>

I also tried to put “slicer.app.connect()”, “reportProgress()” and “onURLReceived()” functions just under the “def setup(self):” line like the example you mentioned - still same error.

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [July 14, 2021, 2:00pm UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/7 "2021-07-14T14:00:21Z")

</div>

You have forgot to add the `self` argument to the reporting function when you made it a class member. I’ve made a few simplifications and uploaded a complete working example here:

> <https://gist.github.com/lassoan/534298ee567000a37b3cb89de01750b8>

[https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/3/4/34513f8646eabef9a132617a9948ae36667a1f85.mp4](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/3/4/34513f8646eabef9a132617a9948ae36667a1f85.mp4)

---

<div class="post-metadata">

**Author:** ![ASH](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/ash/32/11599_2.png) [@ASH](https://discourse.slicer.org/u/ASH)\
**Post date:** [July 15, 2021, 4:41am UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/8 "2021-07-15T04:41:56Z")

</div>

It is working finally! Thank you so much for your support lassoan!

Steps:  
Put LoadRemoteFile.py in `lib\Slicer-4.13\qt-scripted-modules` folder  
Load module from module finder  
Open 3d slicer from slice:// link on browser

I noticed the error “Missing dicomweb\_endpoint”, but guess it’s safe to ignore.

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [July 15, 2021, 4:54am UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/9 "2021-07-15T04:54:19Z")

</div>

> [@ASH](#):
>
> I noticed the error “Missing dicomweb\_endpoint”, but guess it’s safe to ignore.

Yes that’s logged by the DICOM module, as it notices that the application is started with a `slicer://` URL but the link does not point to a DICOMweb server. It is not an actual error, it is safe to ignore it. I’ll change it to a debug-level message.

---

<div class="post-metadata">

**Author:** ![OLIVERFCHEN](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/oliverfchen/32/13069_2.png) [@OLIVERFCHEN](https://discourse.slicer.org/u/OLIVERFCHEN)\
**Post date:** [October 31, 2021, 10:31am UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/10 "2021-10-31T10:31:04Z")

</div>

Hi, I followed the above tutorial but still can’t work.

1. Put LoadRemoteFile.py in `lib\Slicer-4.13\qt-scripted-modules` folder
2. Load module from module finder --in Modules finder “load Remote File” – Switch to module.
3. In Chrome input url: slicer://viewer/?download=https%3A%2F%[2Fgithub.com](http://2Fgithub.com)%2Frbumm%2FSlicerLungCTAnalyzer%2Freleases%2Fdownload%2FSampleData%2FLungCTAnalyzerChestCT.nrrd

nothing happened.

---

<div class="post-metadata">

**Author:** ![muratmaga](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/muratmaga/32/3622_2.png) [@muratmaga](https://discourse.slicer.org/u/muratmaga)\
**Post date:** [November 1, 2021, 12:54am UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/11 "2021-11-01T00:54:12Z")

</div>

İf the script doesnt work for you, there is an `importfromUrl` module in Slicermorph extension.

> **[SlicerMorph/Docs/ImportFromURL at master · SlicerMorph/SlicerMorph](https://github.com/SlicerMorph/SlicerMorph/tree/master/Docs/ImportFromURL)**
>
> master/Docs/ImportFromURL

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [November 1, 2021, 4:16am UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/12 "2021-11-01T04:16:05Z")

</div>

@muratmaga the question is about how to open an image in 3D Slicer by simply clicking on a link in a web browser (user do not have to launch Slicer manually and copy-paste a URL).

> [@OLIVERFCHEN](#):
>
> I followed the above tutorial but still can’t work.

Most likely you have not associated the `slicer://` custom browser protocol with the Slicer application. I’ve added more detailed instructions to the top of the [example module](https://gist.github.com/lassoan/534298ee567000a37b3cb89de01750b8). I’ve just tested this module with the latest Slicer Preview Release on Windows and it worked well.

---

<div class="post-metadata">

**Author:** ![OLIVERFCHEN](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/oliverfchen/32/13069_2.png) [@OLIVERFCHEN](https://discourse.slicer.org/u/OLIVERFCHEN)\
**Post date:** [November 2, 2021, 1:27am UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/13 "2021-11-02T01:27:59Z")

</div>

Thank you for the reply. I reinstalled the latest slicer 4.13.0 and now it worked.  
However, when I download url from DCM4CHEE retrieve link,  
slicer://viewer/?download=http://192.168.8.113:8080/dcm4chee-arc/aets/DCM4CHEE/rs/studies/1.2.276.0.7230010.3.1.4.3928906.148.15392368.470/series/1.2.276.0.7230010.3.1.4.39968906.148.1539239634.469  
It errors out.  
So is it only support nrrd file not dcm?

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [November 2, 2021, 3:27am UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/14 "2021-11-02T03:27:35Z")

</div>

Opening a file via DICOMweb is much simpler, as it is a built-in feature of Slicer’s DICOM module (no need to install a custom module). You need to use the `slicer://viewer/...` URL as described [here](https://slicer.readthedocs.io/en/latest/developer_guide/script_repository/gui.html#launch-slicer-directly-from-a-web-browser).

---

<div class="post-metadata">

**Author:** ![OLIVERFCHEN](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/oliverfchen/32/13069_2.png) [@OLIVERFCHEN](https://discourse.slicer.org/u/OLIVERFCHEN)\
**Post date:** [November 2, 2021, 3:57am UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/15 "2021-11-02T03:57:47Z")

</div>

Thank you, but please bear with me, I still can’t work out.

slicer://viewer/?download=http://192.168.8.113:8080/dcm4chee-arc/aets/DCM4CHEE/rs/studies/1.2.276.0.7230010.3.1.4.3929968906.148.1539239668.470

it did not work, with loading… 0% then  
TypeError: reportProgress() takes 2 positional arguments but 3 were given

or if  
slicer://viewer/?studyUID=1.2.276.0.7230010.3.1.4.1726399104.1960.1540283292.820  
&dicomweb\_endpoint=http://192.168.8.113:8080/dcm4chee-arc/aets/DCM4CHEE/rs  
&dicomweb\_uri\_endpoint=http://192.168.8.113:8080/dcm4chee-arc/aets/DCM4CHEE/rs/studies/1.2.276.0.7230010.3.1.4.1726399104.1960.1540283292.820

i got : NameError: name ‘progressCallback’ is not defined

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [November 2, 2021, 5:36am UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/16 "2021-11-02T05:36:55Z")

</div>

There was an untested code branch (when access token was not specified) that caused the error above. I’ve [fixed it now](https://github.com/Slicer/Slicer/commit/1c513e68183ea2a83b85ad75f43aadb2a2c5a952), you can apply the code change to the same file in your Slicer installation.

---

<div class="post-metadata">

**Author:** ![OLIVERFCHEN](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/oliverfchen/32/13069_2.png) [@OLIVERFCHEN](https://discourse.slicer.org/u/OLIVERFCHEN)\
**Post date:** [November 2, 2021, 10:25am UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/17 "2021-11-02T10:25:31Z")

</div>

Yes, that fixed problem. Thanks.

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [November 2, 2021, 1:08pm UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/18 "2021-11-02T13:08:10Z")

</div>

Great, thanks for the update!

DICOMweb downloads can take a while and we currently only update progress report once for each series. It would be great if you could add more detailed progress reporting to the script in Modules/Scripted/DICOMLib/DICOMUtils.py.

---

<div class="post-metadata">

**Author:** ![OLIVERFCHEN](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/oliverfchen/32/13069_2.png) [@OLIVERFCHEN](https://discourse.slicer.org/u/OLIVERFCHEN)\
**Post date:** [November 3, 2021, 12:11am UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/19 "2021-11-03T00:11:17Z")

</div>

Is this part? I love to do it if you can give me more information.

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/5/a/5aea8bdfa345d5336abeb34ca6050b07700f5e67.png)  
It only shows 0% then jump to 100%, for _len(seriesInstanceUIDs)_ if _seriesInstanceUIDs_ only one then length always 1. So in this _for_ loop, it will not show the values between 0-100.  
The script you mentioned in Modules/Scripted/DICOMLib/DICOMUtils.py, is it a different file?

Also, is that possible to detect whether the slicer already opened before downloading? So that windows will not open slicer multiple instances.

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [November 3, 2021, 5:55pm UTC](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664/20 "2021-11-03T17:55:51Z")

</div>

> [@OLIVERFCHEN](#):
>
> It only shows 0% then jump to 100%, for _len(seriesInstanceUIDs)_ if _seriesInstanceUIDs_ only one then length always 1.

I’ve had a look at the progress reporting and found an easy way to do make it more granular (using the `iter_series` method that was added not too long ago). I’ve also improved error reporting and currently working on selecting the imported series in the DICOM browser. I plan to submit a pull request with these improvements today or tomorrow.

> [@OLIVERFCHEN](#):
>
> is that possible to detect whether the slicer already opened before downloading? So that windows will not open slicer multiple instances.

While there are some operating system level support for singleton applications, there is nothing available on all supported platforms. A nice solution would be to implement a very lightweight launcher application (it could be implemented in Python) that starts Slicer with an extra script. The extra script would create a HTTP server (similarly to how it is done [here](https://github.com/pieper/SlicerWeb/blob/master/WebServer/WebServer.py)) which would listen for HTTP requests. For now the only request that it would need to handle is opening of a URL. After Slicer is started, the launcher application would then send an HTTP request with the URL. The launcher application would always check if the HTTP request is responded and if it is then it would not launch Slicer again, it would just send the new URL to the running Slicer to open.

Since this would not be a high-speed operation, communication with a running Slicer instance could be performed via files. For example, we could have an “incoming” folder within the Slicer install folder that Slicer would monitor and if it detected a new file then it would get the URL from that. However, the HTTP request would be a bit more elegant and versatile (e.g., it could be used more easily in a container or from JavaScript applications).

@pieper What do you think? I know that we could just use SlicerWeb extension as is, but it seems more like a prototyping extension, contains lots of hardcoded, project-specific, experimental things and so it is quite complex. We could probably implement URL opening in 20-30 line of code and it could be part of Slicer core, so it could be used easily in various scenarios. For example SimpleITK notebooks use an environment variable to specify a viewer application. Specifying Slicer directly would mean that for opening each image, users need to wait for Slicer startup, which just takes too long. Specifying the launcher application as viewer would allow opening an image in Slicer very quickly.

[Next page](https://discourse.slicer.org/t/how-to-load-nifti-file-from-web-browser-link/18664.md?page=2)
