diff --git a/opentech/apply/stream_forms/fields.py b/opentech/apply/stream_forms/fields.py
index 7517213d0dd9602724011d1f5a453ab82a96b2d8..6f542f221af663b98bd4ed7ecc69e32c0a8d307e 100644
--- a/opentech/apply/stream_forms/fields.py
+++ b/opentech/apply/stream_forms/fields.py
@@ -1,25 +1,67 @@
-from django.forms import FileInput, FileField
+from django.forms import ClearableFileInput, FileField, CheckboxInput
 
 
-class MultiFileInput(FileInput):
+class MultiFileInput(ClearableFileInput):
     """
     File Input only returns one file from its clean method.
 
     This passes all files through the clean method and means we have a list of
     files available for post processing
     """
-    def __init__(self, *args, attrs={}, **kwargs):
-        attrs['multiple'] = True
-        super().__init__(*args, attrs=attrs, **kwargs)
+    template_name = 'stream_forms/fields/multi_file_field.html'
+
+    input_text = ''
+
+    def __init__(self, *args, **kwargs):
+        self.multiple = kwargs.pop('multiple', True)
+        super().__init__(*args, **kwargs)
+
+    def is_initial(self, value):
+        is_initial = super().is_initial
+        return all(
+            is_initial(file) for file in value
+        )
+
+    def render(self, name, value, attrs=dict()):
+        if self.multiple:
+            attrs['multiple'] = 'multiple'
+
+        return super().render(name, value, attrs)
 
     def value_from_datadict(self, data, files, name):
-        return files.getlist(name)
+        if hasattr(files, 'getlist'):
+            upload = files.getlist(name)
+        else:
+            upload = files.get(name)
+            if not isinstance(upload, list):
+                upload = [upload]
+
+        checkbox_name = self.clear_checkbox_name(name) + '-'
+        checkboxes = {k for k in data if checkbox_name in k}
+        cleared = {
+            int(checkbox.replace(checkbox_name, '')) for checkbox in checkboxes
+            if CheckboxInput().value_from_datadict(data, files, checkbox)
+        }
+
+        return {
+            'files': upload,
+            'cleared': cleared,
+        }
 
 
 class MultiFileField(FileField):
     widget = MultiFileInput
 
     def clean(self, value, initial):
-        if not value:
+        files = value['files']
+        cleared = value['cleared']
+        if not files and not cleared:
             return initial
-        return [FileField().clean(file, initial) for file in value]
+        new = [FileField().clean(file, initial) for file in files]
+
+        if initial:
+            old = [file for i, file in enumerate(initial) if i not in cleared]
+        else:
+            old = []
+
+        return old + new
diff --git a/opentech/apply/stream_forms/files.py b/opentech/apply/stream_forms/files.py
index e31b07cf76dc86a772b750c97145ef4493fbe8fb..1d5b8ce5d7df2b780b2e70269fa8bffc227dfe73 100644
--- a/opentech/apply/stream_forms/files.py
+++ b/opentech/apply/stream_forms/files.py
@@ -22,8 +22,15 @@ class StreamFieldFile(File):
         self.filename = filename or os.path.basename(self.name)
         self._committed = False
 
+    def __str__(self):
+        return self.filename
+
     def __eq__(self, other):
-        return self.filename == other.filename and self.size == other.size
+        if isinstance(other, File):
+            return self.filename == other.filename and self.size == other.size
+        # Rely on the other object to know how to check equality
+        # Could cause infinite loop if the other object is unsure how to compare
+        return other.__eq__(self)
 
     def _get_file(self):
         if getattr(self, '_file', None) is None:
diff --git a/opentech/apply/stream_forms/templates/stream_forms/fields/multi_file_field.html b/opentech/apply/stream_forms/templates/stream_forms/fields/multi_file_field.html
new file mode 100644
index 0000000000000000000000000000000000000000..e48370c3e80349b72ab6c42389e0379262f721c8
--- /dev/null
+++ b/opentech/apply/stream_forms/templates/stream_forms/fields/multi_file_field.html
@@ -0,0 +1,17 @@
+{% if widget.is_initial %}{{ widget.initial_text }}:
+<p>
+{{ widget.clear_checkbox_label }}
+</p>
+{% for file in widget.value %}
+<p>
+    {% with y=forloop.counter0|stringformat:"s" %}
+    {% with file_id=widget.checkbox_name|add:'-'|add:y %}
+        <input type="checkbox" name="{{ file_id }}" id="{{ file_id }}">
+        <label for="{{ file_id }}"></label>
+    {% endwith %}
+    {% endwith %}
+    <a href="{{ file.url }}">{{ file }}</a>
+</p>
+{% endfor %}
+{{ widget.input_text }}{% endif %}
+<input type="{{ widget.type }}" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>
diff --git a/opentech/apply/stream_forms/tests.py b/opentech/apply/stream_forms/tests.py
index a79ca8be565f44aacce95bad20c1ee34d175ed20..5bdf4b189f2951344bc06e496c9bf9a4f9a2283a 100644
--- a/opentech/apply/stream_forms/tests.py
+++ b/opentech/apply/stream_forms/tests.py
@@ -1,3 +1,89 @@
-# from django.test import TestCase
+from django.core.files.uploadedfile import SimpleUploadedFile
+from django.test import TestCase
 
-# Create your tests here.
+from faker import Faker
+
+from .files import StreamFieldFile
+from .fields import MultiFileField, MultiFileInput
+
+fake = Faker()
+
+
+def make_files(number):
+    file_names = [f'{fake.word()}_{i}' for i in range(3)]
+    files = [
+        StreamFieldFile(SimpleUploadedFile(name, b'Some File Content'), filename=name)
+        for name in file_names
+    ]
+    return files
+
+
+class TestMultiFileInput(TestCase):
+    widget = MultiFileInput()
+
+    def test_renders_multiple_attr(self):
+        html = self.widget.render('', [])
+        self.assertIn('multiple', html)
+
+    def test_renders_multiple_files(self):
+        files = make_files(3)
+        html = self.widget.render('', files)
+        for file in files:
+            self.assertIn(file.filename, html)
+
+    def test_handles_files(self):
+        field_name = 'testing'
+        files = make_files(3)
+        files_data = {field_name: files}
+        data = self.widget.value_from_datadict({}, files_data, field_name)
+        self.assertEqual(data['files'], files)
+
+    def test_no_delete(self):
+        data = self.widget.value_from_datadict({}, {}, '')
+        self.assertFalse(data['cleared'])
+
+    def test_delete(self):
+        field_name = 'testing'
+        field_id = self.widget.clear_checkbox_name(field_name) + '-'
+        form_data = {
+            field_id + '0': 'on',
+            field_id + '4': 'on',
+        }
+        data = self.widget.value_from_datadict(form_data, {}, field_name)
+        self.assertEqual(data['cleared'], {0, 4})
+
+
+class TestMultiFileField(TestCase):
+    field = MultiFileField()
+
+    def multi_file_value(self, files=list(), cleared=set()):
+        return {
+            'files': files,
+            'cleared': cleared,
+        }
+
+    def test_returns_files_if_no_change(self):
+        files = make_files(3)
+        cleaned = self.field.clean(self.multi_file_value(), files)
+        self.assertEqual(files, cleaned)
+
+    def test_returns_new_files(self):
+        files = make_files(3)
+        cleaned = self.field.clean(self.multi_file_value(files=files), None)
+        self.assertEqual(files, cleaned)
+
+    def test_returns_inital_and_files(self):
+        initial_files = make_files(3)
+        new_files = make_files(3)
+        cleaned = self.field.clean(self.multi_file_value(files=new_files), initial_files)
+        self.assertEqual(initial_files + new_files, cleaned)
+
+    def test_returns_nothing_all_cleared(self):
+        initial_files = make_files(3)
+        cleaned = self.field.clean(self.multi_file_value(cleared=range(3)), initial_files)
+        self.assertEqual([], cleaned)
+
+    def test_returns_something_some_cleared(self):
+        initial_files = make_files(3)
+        cleaned = self.field.clean(self.multi_file_value(cleared=[0, 2]), initial_files)
+        self.assertEqual([initial_files[1]], cleaned)