Repository: scikit-image/scikit-image

Issue:
"[Errno 36] File name too long:" when using imread on remote resource with long querystring
## Description
When using skimage.io.imread with a remote resource, a long query string on the remote resource will cause a failure to read the remote resource, because the temporary file cannot be created.

e.g. 

The following works fine
```
>>> im = imread('https://c1.staticflickr.com/9/8370/8429454143_1066b73c04_o.jpg?{}'.format(''.join(['s' for i in range(100)])))

```

while the one below fails

```
>>> im = imread('https://c1.staticflickr.com/9/8370/8429454143_1066b73c04_o.jpg?{}'.format(''.join(['s' for i in range(300)])))
Traceback (most recent call last):
  File "/usr/local/lib/python3.5/dist-packages/skimage/io/util.py", line 28, in file_or_url_context
    with tempfile.NamedTemporaryFile(delete=False, suffix=ext) as f:
  File "/usr/lib/python3.5/tempfile.py", line 688, in NamedTemporaryFile
    (fd, name) = _mkstemp_inner(dir, prefix, suffix, flags, output_type)
  File "/usr/lib/python3.5/tempfile.py", line 399, in _mkstemp_inner
    fd = _os.open(file, flags, 0o600)
OSError: [Errno 36] File name too long: '/tmp/tmpmfnujlq6.jpg?ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.5/dist-packages/skimage/io/_io.py", line 61, in imread
    with file_or_url_context(fname) as fname:
  File "/usr/lib/python3.5/contextlib.py", line 59, in __enter__
    return next(self.gen)
  File "/usr/local/lib/python3.5/dist-packages/skimage/io/util.py", line 34, in file_or_url_context
    os.remove(f.name)
UnboundLocalError: local variable 'f' referenced before assignment

```

## Way to reproduce
[If reporting a bug, please include the following important information:]
- [x] Code example
- [x] Operating system and version
```
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.5 LTS"
```
- [x] Python version: Python 3.5.2
- [x] scikit-image version (run `skimage.__version__`): skimage.__version__
'0.14.1'

## Proposal

https://github.com/scikit-image/scikit-image/blob/d24634d86e4f90fe96377209d66ed114b9b601e4/skimage/io/util.py#L22

The querystring should be excluded from the temporary file name extension


Discussion:
@jo-tham yikes, good catch! Do you know how to submit a pull request to make that change? I agree with your suggestion btw.

Are you trying to get your pics out of Flickr before they delete them? :joy: 

btw, I just thought I'd check and indeed this works:

```python
In [1]: 's' * 5
Out[1]: 'sssss'
```
Hi @jni - thanks for the quick reply (and the observation on `string * int`)

Hehe, I didn't know flickr is changing, that example was a coincidence, I just searched for some CC images.

I ran into the issue as I maintain services that use signed urls for s3 resources. I have not run into this in the live services, but I was trying different signing methods and this issue came up.

Yep, I can submit a PR first thing tomorrow (pacific time). 

Resolving patch:
diff --git a/skimage/io/util.py b/skimage/io/util.py
--- a/skimage/io/util.py
+++ b/skimage/io/util.py
@@ -1,4 +1,4 @@
-from urllib.request import urlopen
+import urllib.parse
 
 import os
 import re
@@ -19,10 +19,11 @@ def is_url(filename):
 def file_or_url_context(resource_name):
     """Yield name of file from the given resource (i.e. file or url)."""
     if is_url(resource_name):
-        _, ext = os.path.splitext(resource_name)
+        url_components = urllib.parse.urlparse(resource_name)
+        _, ext = os.path.splitext(url_components.path)
         try:
             with tempfile.NamedTemporaryFile(delete=False, suffix=ext) as f:
-                u = urlopen(resource_name)
+                u = urllib.request.urlopen(resource_name)
                 f.write(u.read())
             # f must be closed before yielding
             yield f.name


Test patch:
diff --git a/skimage/io/tests/test_io.py b/skimage/io/tests/test_io.py
--- a/skimage/io/tests/test_io.py
+++ b/skimage/io/tests/test_io.py
@@ -1,5 +1,6 @@
 import os
 
+import pytest
 import numpy as np
 from skimage import io, data_dir
 
@@ -7,6 +8,30 @@
 from skimage._shared.testing import assert_array_equal
 
 
+@pytest.fixture
+def urllib_urlopen(monkeypatch):
+    def mockopen(resource_name):
+        class MockOpen:
+            @staticmethod
+            def read():
+                # bytes of a 1x1 jpg
+                return (b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01'
+                        b'\x00\x01\x00\x00\xff\xdb\x00C\x00\x03\x02\x02\x02\x02'
+                        b'\x02\x03\x02\x02\x02\x03\x03\x03\x03\x04\x06\x04\x04'
+                        b'\x04\x04\x04\x08\x06\x06\x05\x06\t\x08\n\n\t\x08\t\t'
+                        b'\n\x0c\x0f\x0c\n\x0b\x0e\x0b\t\t\r\x11\r\x0e\x0f\x10'
+                        b'\x10\x11\x10\n\x0c\x12\x13\x12\x10\x13\x0f\x10\x10'
+                        b'\x10\xff\xc0\x00\x0b\x08\x00\x01\x00\x01\x01\x01\x11'
+                        b'\x00\xff\xc4\x00\x14\x00\x01\x00\x00\x00\x00\x00\x00'
+                        b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\xff\xc4\x00'
+                        b'\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+                        b'\x00\x00\x00\x00\x00\x00\xff\xda\x00\x08\x01\x01\x00'
+                        b'\x00?\x00*\x9f\xff\xd9')
+        return MockOpen
+
+    monkeypatch.setattr('urllib.request.urlopen', mockopen)
+
+
 def test_stack_basic():
     x = np.arange(12).reshape(3, 4)
     io.push(x)
@@ -19,10 +44,17 @@ def test_stack_non_array():
         io.push([[1, 2, 3]])
 
 
-def test_imread_url():
+def test_imread_file_url():
     # tweak data path so that file URI works on both unix and windows.
     data_path = data_dir.lstrip(os.path.sep)
     data_path = data_path.replace(os.path.sep, '/')
     image_url = 'file:///{0}/camera.png'.format(data_path)
     image = io.imread(image_url)
     assert image.shape == (512, 512)
+
+
+def test_imread_http_url(urllib_urlopen):
+    query_string = '?' + 's' * 266
+    image_url = 'https://image.com/camera.jpg{}'.format(query_string)
+    image = io.imread(image_url)
+    assert image.shape == (1, 1)