diff --git a/pyclarity_lims/lims.py b/pyclarity_lims/lims.py
index 4f64117..67f26ad 100644
--- a/pyclarity_lims/lims.py
+++ b/pyclarity_lims/lims.py
@@ -89,18 +89,21 @@ class Lims(object):
         else:
             return self.parse_response(r)
 
-    def get_file_contents(self, id=None, uri=None):
+    def get_file_contents(self, id=None, uri=None, encoding=None, crlf=False):
         """Returns the contents of the file of <ID> or <uri>"""
         if id:
-            segments = ['api', self.VERSION, 'files', id, 'download']
+            url = self.get_uri('files', id, 'download')
         elif uri:
-            segments = [uri, 'download']
+            url = uri.rstrip('/') + '/download'
         else:
-            raise ValueError("id or uri required")
-        url = urljoin(self.baseuri, '/'.join(segments))
+            raise ValueError('id or uri required')
+
         r = self.request_session.get(url, auth=(self.username, self.password), timeout=TIMEOUT)
         self.validate_response(r)
-        return r.text
+        if encoding:
+            r.encoding = encoding
+
+        return r.text.replace('\r\n', '\n') if crlf else r.text
 
     def upload_new_file(self, entity, file_to_upload):
         """Upload a file and attach it to the provided entity."""

Support for different file encodings
We should be able to read `utf-16`-encoded files, e.g. from the Spectramax. `requests` can return text with any encoding:

```python
>>> request.encoding
'utf-8'
>>> request.text
'some_glitchy_looking_binary_data'
>>> request.encoding = 'utf-16'
>>> request.text
'some_well_formed_data'
```

We should add this to `Lims.get_file_contents`. We should also be able to replace CRLFs with `\n`.

diff --git a/tests/test_lims.py b/tests/test_lims.py
index ffa503b..bcf82db 100644
--- a/tests/test_lims.py
+++ b/tests/test_lims.py
@@ -1,4 +1,3 @@
-import xml
 from unittest import TestCase
 
 from requests.exceptions import HTTPError
@@ -19,6 +18,7 @@ else:
     from unittest.mock import patch, Mock
     import builtins
 
+
 class TestLims(TestCase):
     url = 'http://testgenologics.com:4040'
     username = 'test'
@@ -36,12 +36,10 @@ class TestLims(TestCase):
 <exc:exception xmlns:exc="http://pyclarity_lims.com/ri/exception">
 </exc:exception>"""
 
-
     def test_get_uri(self):
         lims = Lims(self.url, username=self.username, password=self.password)
         assert lims.get_uri('artifacts',sample_name='test_sample') == '{url}/api/v2/artifacts?sample_name=test_sample'.format(url=self.url)
 
-
     def test_parse_response(self):
         lims = Lims(self.url, username=self.username, password=self.password)
         r = Mock(content = self.sample_xml, status_code=200)
@@ -56,7 +54,6 @@ class TestLims(TestCase):
         r = Mock(content = self.error_no_msg_xml, status_code=400)
         self.assertRaises(HTTPError, lims.parse_response, r)
 
-
     @patch('requests.Session.get',return_value=Mock(content = sample_xml, status_code=200))
     def test_get(self, mocked_instance):
         lims = Lims(self.url, username=self.username, password=self.password)
@@ -88,7 +85,6 @@ class TestLims(TestCase):
             self.assertRaises(HTTPError, lims.post, uri=uri, data=self.sample_xml)
             assert mocked_put.call_count == 1
 
-
     @patch('os.path.isfile', return_value=True)
     @patch.object(builtins, 'open')
     def test_upload_new_file(self, mocked_open, mocked_isfile):
@@ -124,8 +120,6 @@ class TestLims(TestCase):
         lims.route_artifacts(artifact_list=[artifact], workflow_uri=self.url+'/api/v2/configuration/workflows/1')
         assert mocked_post.call_count == 1
 
-
-
     def test_tostring(self):
         lims = Lims(self.url, username=self.username, password=self.password)
         from xml.etree import ElementTree as ET
@@ -139,5 +133,16 @@ class TestLims(TestCase):
         string = lims.tostring(etree)
         assert string == expected_string
 
+    def test_get_file_contents(self):
+        lims = Lims(self.url, username=self.username, password=self.password)
+        lims.validate_response = Mock()
+        lims.request_session = Mock(get=Mock(return_value=Mock(encoding=None, text='some data\r\n')))
+        exp_url = self.url + '/api/v2/files/an_id/download'
 
+        assert lims.get_file_contents(uri=self.url + '/api/v2/files/an_id') == 'some data\r\n'
+        assert lims.request_session.get.return_value.encoding is None
+        lims.request_session.get.assert_called_with(exp_url, auth=(self.username, self.password), timeout=16)
 
+        assert lims.get_file_contents(id='an_id', encoding='utf-16', crlf=True) == 'some data\n'
+        assert lims.request_session.get.return_value.encoding == 'utf-16'
+        lims.request_session.get.assert_called_with(exp_url, auth=(self.username, self.password), timeout=16)

pip install -e .
parse_log_pytest
requirements.txt
pytest
apt-get update apt-get install -y gcc
3.6
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
certifi==2021.5.30
charset-normalizer==2.0.12
idna==3.10
importlib-metadata==4.8.3
iniconfig==1.1.1
packaging==21.3
pluggy==1.0.0
py==1.11.0
-e git+https://github.com/EdinburghGenomics/pyclarity-lims.git@14dae08ac127b86df96b326ce466d4c4854dff2c#egg=pyclarity_lims
pyparsing==3.1.4
pytest==7.0.1
requests==2.27.1
tomli==1.2.3
typing_extensions==4.1.1
urllib3==1.26.20
zipp==3.6.0

name: pyclarity-lims
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - charset-normalizer==2.0.12
      - idna==3.10
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - requests==2.27.1
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - zipp==3.6.0
prefix: /opt/conda/envs/pyclarity-lims

tests/test_lims.py::TestLims::test_get_file_contents
tests/test_lims.py::TestLims::test_get tests/test_lims.py::TestLims::test_get_uri tests/test_lims.py::TestLims::test_parse_response tests/test_lims.py::TestLims::test_post tests/test_lims.py::TestLims::test_put tests/test_lims.py::TestLims::test_route_artifact tests/test_lims.py::TestLims::test_tostring tests/test_lims.py::TestLims::test_upload_new_file
pyclarity_lims/lims.py
pyclarity_lims/lims.py