diff --git a/nylas/client/restful_models.py b/nylas/client/restful_models.py
index d8d1c98..7f31653 100644
--- a/nylas/client/restful_models.py
+++ b/nylas/client/restful_models.py
@@ -387,18 +387,27 @@ class File(NylasAPIObject):
     collection_name = 'files'
 
     def save(self):  # pylint: disable=arguments-differ
-        if hasattr(self, 'stream') and self.stream is not None:
-            data = {self.filename: self.stream}
-        elif hasattr(self, 'data') and self.data is not None:
-            data = {self.filename: StringIO(self.data)}
-        else:
+        stream = getattr(self, "stream", None)
+        if not stream:
+            data = getattr(self, "data", None)
+            if data:
+                stream = StringIO(data)
+
+        if not stream:
             message = (
                 "File object not properly formatted, "
                 "must provide either a stream or data."
             )
             raise FileUploadError(message=message)
 
-        new_obj = self.api._create_resources(File, data)
+        file_info = (
+            self.filename,
+            stream,
+            self.content_type,
+            {}, # upload headers
+        )
+
+        new_obj = self.api._create_resources(File, {"file": file_info})
         new_obj = new_obj[0]
         for attr in self.attrs:
             if hasattr(new_obj, attr):

File.filename doesn't apply when type is stream:
Setting a filename on an attachment only works when using `.data`. When `.stream` is used, it falls back to the name of the file on the uploading system.

```
        myfile = self.nylas_client.files.create()
        myfile.content_type = 'application/pdf'
        myfile.filename = attachment_name
        with open(attachment_path, 'rb') as f:
            myfile.stream = f
            myfile.save()

        myfile.filename = attachment_name
        # Create a new draft
        draft = self.nylas_client.drafts.create()
        if type(recipients) == str:
            recipients = [recipients]
        draft.to = [{'email': recipient} for recipient in recipients]
        draft.subject = subject
        draft.body = message
        draft.attach(myfile)
```
diff --git a/tests/conftest.py b/tests/conftest.py
index f8fb9ad..4e8eb72 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,6 +1,7 @@
 import re
 import json
 import copy
+import cgi
 import random
 import string
 import pytest
@@ -674,24 +675,59 @@ def mock_draft_sent_response(mocked_responses, api_url):
 
 
 @pytest.fixture
-def mock_files(mocked_responses, api_url):
-    body = [{
-        "content_type": "text/plain",
-        "filename": "a.txt",
-        "id": "3qfe4k3siosfjtjpfdnon8zbn",
-        "account_id": "6aakaxzi4j5gn6f7kbb9e0fxs",
-        "object": "file",
-        "size": 762878
-    }]
+def mock_files(mocked_responses, api_url, account_id):
+    files_content = {
+        "3qfe4k3siosfjtjpfdnon8zbn": b"Hello, World!",
+    }
+    files_metadata = {
+        "3qfe4k3siosfjtjpfdnon8zbn": {
+            "id": "3qfe4k3siosfjtjpfdnon8zbn",
+            "content_type": "text/plain",
+            "filename": "hello.txt",
+            "account_id": account_id,
+            "object": "file",
+            "size": len(files_content["3qfe4k3siosfjtjpfdnon8zbn"])
+        }
+    }
     mocked_responses.add(
+        responses.GET,
+        api_url + '/files',
+        body=json.dumps(list(files_metadata.values())),
+    )
+    for file_id in files_content:
+        mocked_responses.add(
+            responses.POST,
+            "{base}/files/{file_id}".format(base=api_url, file_id=file_id),
+            body=json.dumps(files_metadata[file_id]),
+        )
+        mocked_responses.add(
+            responses.GET,
+            "{base}/files/{file_id}/download".format(base=api_url, file_id=file_id),
+            body=files_content[file_id],
+        )
+
+    def create_callback(request):
+        uploaded_lines = request.body.decode('utf8').splitlines()
+        content_disposition = uploaded_lines[1]
+        _, params = cgi.parse_header(content_disposition)
+        filename = params.get("filename", None)
+        content = "".join(uploaded_lines[3:-1])
+        size = len(content.encode('utf8'))
+
+        body = [{
+            "id": generate_id(),
+            "content_type": "text/plain",
+            "filename": filename,
+            "account_id": account_id,
+            "object": "file",
+            "size": size,
+        }]
+        return (200, {}, json.dumps(body))
+
+    mocked_responses.add_callback(
         responses.POST,
         api_url + '/files/',
-        body=json.dumps(body),
-    )
-    mocked_responses.add(
-        responses.GET,
-        api_url + '/files/3qfe4k3siosfjtjpfdnon8zbn/download',
-        body='test body',
+        callback=create_callback,
     )
 
 
diff --git a/tests/test_files.py b/tests/test_files.py
index 046c274..8da27fa 100644
--- a/tests/test_files.py
+++ b/tests/test_files.py
@@ -1,19 +1,65 @@
+import cgi
+from io import BytesIO
 import pytest
 from nylas.client.errors import FileUploadError
 
 
 @pytest.mark.usefixtures("mock_files")
-def test_file_upload(api_client):
+def test_file_upload_data(api_client, mocked_responses):
+    data = "Hello, World!"
+
     myfile = api_client.files.create()
-    myfile.filename = 'test.txt'
-    myfile.data = "Hello World."
+    myfile.filename = 'hello.txt'
+    myfile.data = data
+
+    assert not mocked_responses.calls
     myfile.save()
+    assert len(mocked_responses.calls) == 1
+
+    assert myfile.filename == 'hello.txt'
+    assert myfile.size == 13
 
-    assert myfile.filename == 'a.txt'
-    assert myfile.size == 762878
+    upload_body = mocked_responses.calls[0].request.body
+    upload_lines = upload_body.decode("utf8").splitlines()
+
+    content_disposition = upload_lines[1]
+    _, params = cgi.parse_header(content_disposition)
+    assert params["filename"] == "hello.txt"
+    assert "Hello, World!" in upload_lines
+
+
+@pytest.mark.usefixtures("mock_files")
+def test_file_upload_stream(api_client, mocked_responses):
+    stream = BytesIO(b"Hello, World!")
+    stream.name = "wacky.txt"
 
+    myfile = api_client.files.create()
+    myfile.filename = 'hello.txt'
+    myfile.stream = stream
+    assert not mocked_responses.calls
+    myfile.save()
+    assert len(mocked_responses.calls) == 1
+
+    assert myfile.filename == 'hello.txt'
+    assert myfile.size == 13
+
+    upload_body = mocked_responses.calls[0].request.body
+    upload_lines = upload_body.decode("utf8").splitlines()
+
+    content_disposition = upload_lines[1]
+    _, params = cgi.parse_header(content_disposition)
+    assert params["filename"] == "hello.txt"
+    assert "Hello, World!" in upload_lines
+
+
+@pytest.mark.usefixtures("mock_files")
+def test_file_download(api_client, mocked_responses):
+    assert not mocked_responses.calls
+    myfile = api_client.files.first()
+    assert len(mocked_responses.calls) == 1
     data = myfile.download().decode()
-    assert data == 'test body'
+    assert len(mocked_responses.calls) == 2
+    assert data == "Hello, World!"
 
 
 def test_file_invalid_upload(api_client):

pip install -e .[test]
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-pylint responses
apt-get update apt-get install -y gcc
3.6
examples/requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
astroid==2.11.7
attrs==22.2.0
bump2version==1.0.1
bumpversion==0.6.0
certifi==2021.5.30
cffi==1.15.1
charset-normalizer==2.0.12
cookies==2.2.1
coverage==6.2
cryptography==40.0.2
dill==0.3.4
idna==3.10
importlib-metadata==4.8.3
iniconfig==1.1.1
isort==5.10.1
lazy-object-proxy==1.7.1
mccabe==0.7.0
ndg-httpsclient==0.5.1
-e git+https://github.com/nylas/nylas-python.git@068a626706188eea00b606608ab06c9b9ea307fb#egg=nylas
packaging==21.3
platformdirs==2.4.0
pluggy==1.0.0
py==1.11.0
pyasn1==0.5.1
pycparser==2.21
pylint==2.13.9
pyOpenSSL==23.2.0
pyparsing==3.1.4
pytest==7.0.1
pytest-cov==4.0.0
pytest-pylint==0.18.0
requests==2.27.1
responses==0.6.1
six==1.17.0
toml==0.10.2
tomli==1.2.3
typed-ast==1.5.5
typing_extensions==4.1.1
urllib3==1.26.20
URLObject==2.4.3
wrapt==1.16.0
zipp==3.6.0

name: nylas-python
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:
      - astroid==2.11.7
      - attrs==22.2.0
      - bump2version==1.0.1
      - bumpversion==0.6.0
      - cffi==1.15.1
      - charset-normalizer==2.0.12
      - cookies==2.2.1
      - coverage==6.2
      - cryptography==40.0.2
      - dill==0.3.4
      - idna==3.10
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - isort==5.10.1
      - lazy-object-proxy==1.7.1
      - mccabe==0.7.0
      - ndg-httpsclient==0.5.1
      - packaging==21.3
      - platformdirs==2.4.0
      - pluggy==1.0.0
      - py==1.11.0
      - pyasn1==0.5.1
      - pycparser==2.21
      - pylint==2.13.9
      - pyopenssl==23.2.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytest-cov==4.0.0
      - pytest-pylint==0.18.0
      - requests==2.27.1
      - responses==0.6.1
      - six==1.17.0
      - toml==0.10.2
      - tomli==1.2.3
      - typed-ast==1.5.5
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - urlobject==2.4.3
      - wrapt==1.16.0
      - zipp==3.6.0
prefix: /opt/conda/envs/nylas-python

tests/test_files.py::test_file_upload_stream
tests/test_files.py::test_file_upload_data tests/test_files.py::test_file_download tests/test_files.py::test_file_invalid_upload tests/test_files.py::test_file_upload_errors
nylas/client/restful_models.py
nylas/client/restful_models.py