diff --git a/src/main/java/de/deadlocker8/budgetmaster/images/ImageService.java b/src/main/java/de/deadlocker8/budgetmaster/images/ImageService.java
index cd364e3e69e49b338ef76e51da5da0eb15d382c6..6dba1b71474ef7b1dd976684c19c9356991c04b1 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/images/ImageService.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/images/ImageService.java
@@ -5,6 +5,11 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.Optional;
 
 @Service
 public class ImageService implements Resetable
@@ -46,6 +51,32 @@ public class ImageService implements Resetable
 //			setAsDefaultAccount(account.getID());
 //			LOGGER.debug("Created default account");
 		}
+	}
+
+	@Transactional
+	public void saveImageFile(MultipartFile file) throws IOException
+	{
+		Byte[] byteObjects = new Byte[file.getBytes().length];
+
+		int i = 0;
+		for(byte b : file.getBytes())
+		{
+			byteObjects[i++] = b;
+		}
+
+		final Optional<String> fileExtensionOptional = getFileExtension(file.getOriginalFilename());
+		if(fileExtensionOptional.isEmpty())
+		{
+			throw new IllegalArgumentException("Could not determine file extension from file name: " + file.getOriginalFilename());
+		}
+
+		final Image image = new Image(byteObjects, fileExtensionOptional.get());
+		imageRepository.save(image);
+	}
 
+	private Optional<String> getFileExtension(String filename) {
+		return Optional.ofNullable(filename)
+				.filter(f -> f.contains("."))
+				.map(f -> f.substring(filename.lastIndexOf(".") + 1));
 	}
 }
diff --git a/src/main/java/de/deadlocker8/budgetmaster/images/MediaController.java b/src/main/java/de/deadlocker8/budgetmaster/images/MediaController.java
index f3db7d452eb91466975cae9613545c3d71081682..41fb1c6014ae4a4fe34cbe33bede87ee24b0dd6b 100644
--- a/src/main/java/de/deadlocker8/budgetmaster/images/MediaController.java
+++ b/src/main/java/de/deadlocker8/budgetmaster/images/MediaController.java
@@ -1,16 +1,17 @@
 package de.deadlocker8.budgetmaster.images;
 
+import com.google.gson.JsonObject;
 import de.deadlocker8.budgetmaster.controller.BaseController;
 import de.deadlocker8.budgetmaster.utils.Mappings;
+import de.thecodelabs.utils.util.Localization;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
+import java.io.IOException;
+
 @Controller
 @RequestMapping(Mappings.MEDIA)
 public class MediaController extends BaseController
@@ -31,9 +32,27 @@ public class MediaController extends BaseController
 	}
 
 	@PostMapping("uploadImage")
-	public String handleImagePost(@RequestParam("file") MultipartFile file)
+	@ResponseBody
+	public String uploadImage(@RequestParam("file") MultipartFile file)
 	{
-		imageService.saveImageFile(file);
-		return "redirect:/accounts";
+		boolean success = true;
+		String localizedMessage = Localization.getString("upload.image.success");
+
+		try
+		{
+			imageService.saveImageFile(file);
+		}
+		catch(IOException e)
+		{
+			e.printStackTrace();
+			success = false;
+			localizedMessage = Localization.getString("upload.image.error", e.getMessage());
+		}
+
+		final JsonObject data = new JsonObject();
+		data.addProperty("isUploadSuccessful", success);
+		data.addProperty("localizedMessage", localizedMessage);
+
+		return data.toString();
 	}
 }
diff --git a/src/main/resources/languages/base_de.properties b/src/main/resources/languages/base_de.properties
index 374f16f31164f7076bbf93101a212fdcf1312f10..e44b9ba9101ef9af515cf508be56e2eeeca0e855 100644
--- a/src/main/resources/languages/base_de.properties
+++ b/src/main/resources/languages/base_de.properties
@@ -139,6 +139,8 @@ notification.settings.saved=Einstellungen gespeichert
 notification.settings.update.available=BudgetMaster Update "{0}" verfügbar
 notification.settings.database.delete.success=Datenbank erfolgreich gelöscht
 notification.settings.database.import.success=Import erfolgreich: {0} Konten, {1} Buchungen, {2} Kategorien, {3} Vorlagen und {4} Diagramme
+upload.image.success=Erfolgreich hochgeladen
+upload.image.error=Fehler: {0}
 
 # WARNING
 warning.text.account.delete=Das Konto "{0}" kann nicht gelöscht werden, da mindestens ein Konto existieren muss. Um dieses Konto zu löschen musst du zuerst ein neues anlegen.
diff --git a/src/main/resources/languages/base_en.properties b/src/main/resources/languages/base_en.properties
index 48e7dfb924ad2c9e7b7c9980ba3d1dd90521d852..cca8b4d17af55634268520ba673e4fc59c22d4d8 100644
--- a/src/main/resources/languages/base_en.properties
+++ b/src/main/resources/languages/base_en.properties
@@ -139,6 +139,8 @@ notification.settings.saved=Settings saved
 notification.settings.update.available=BudgetMaster update "{0}" available
 notification.settings.database.delete.success=Successfully deleted database
 notification.settings.database.import.success=Import successful: {0} accounts, {1} transactions, {2} categories, {3} templates and {4} charts
+upload.image.success=Upload successful
+upload.image.error=Error: {0}
 
 # WARNING
 warning.text.account.delete=The account "{0}" could not be deleted, because at least one account must exist at all time. You have to create a new account in order to delete this one.
diff --git a/src/main/resources/static/js/accounts.js b/src/main/resources/static/js/accounts.js
index 150f2e8a78476d58111cfaff560fcaafa27dcb0c..7987917809c2ae8f2616fd93ef4668e08cf552e0 100644
--- a/src/main/resources/static/js/accounts.js
+++ b/src/main/resources/static/js/accounts.js
@@ -56,6 +56,11 @@ $(document).ready(function()
     {
         openSelectAccountIconModal(this);
     });
+
+    $('#button-upload-new-image').click(function()
+    {
+        uploadImage();
+    });
 });
 
 function openSelectAccountIconModal(item)
@@ -72,7 +77,7 @@ function openSelectAccountIconModal(item)
             // select an icon option
             $('.account-icon-option').click(function()
             {
-               selectIcon(this);
+                selectIcon(this);
             });
 
             $(modalID).modal();
@@ -90,4 +95,37 @@ function selectIcon(item)
     }
 
     item.classList.add('selected');
+}
+
+function uploadImage()
+{
+    let formID = 'form-upload-account-image';
+    let form = document.getElementById(formID);
+
+    $.ajax({
+        url: form.action,
+        enctype: 'multipart/form-data',
+        type: 'post',
+        processData: false,
+        contentType: false,
+        cache: false,
+        data: new FormData(form),
+        success: function(response)
+        {
+            let parsedData = JSON.parse(response);
+            let isUploadSuccessful = parsedData['isUploadSuccessful']
+            M.toast({
+                html: parsedData['localizedMessage'],
+                classes: isUploadSuccessful ? 'green' : 'red'
+            });
+        },
+        error: function(response)
+        {
+            let parsedData = JSON.parse(response);
+            M.toast({
+                html: parsedData['localizedMessage'],
+                classes: 'red'
+            });
+        }
+    });
 }
\ No newline at end of file
diff --git a/src/main/resources/templates/accounts/accountFunctions.ftl b/src/main/resources/templates/accounts/accountFunctions.ftl
index 78eb9c1d4f97b71045752a7fe0425b6c604454e2..e03a5bfffa50c5526ec483cd8afcffaa353774d6 100644
--- a/src/main/resources/templates/accounts/accountFunctions.ftl
+++ b/src/main/resources/templates/accounts/accountFunctions.ftl
@@ -1,3 +1,4 @@
+<#import "/spring.ftl" as s>
 <#import "../helpers/header.ftl" as header>
 
 <#macro modalAccountIconSelect>
@@ -32,7 +33,8 @@
 </#macro>
 
 <#macro uploadImageForm>
-    <form id="form-upload-account-image" method="POST" action="/media" enctype="multipart/form-data">
+    <form></form>
+    <form id="form-upload-account-image" method="post" action="<@s.url '/media/uploadImage'/>" enctype="multipart/form-data">
         <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
         <div class="file-field input-field col s12">
             <div class="container">
@@ -41,10 +43,10 @@
                     ${locale.getString("account.new.icon.upload.choose.file")}
                     <input id="inputUploadFile" type="file" accept="image/png, image/jpeg" name="file">
                 </div>
-                <div class="file-path-wrapper" id="abc">
-                    <input id="def" class="file-path validate" type="text">
+                <div class="file-path-wrapper">
+                    <input class="file-path validate" type="text">
                 </div>
-                <@header.buttonSubmit name='action' icon='upload' localizationKey='account.new.icon.upload' classes="right"/>
+                <@header.buttonLink url='' icon='upload' localizationKey='account.new.icon.upload' id='button-upload-new-image' classes='right' noUrl=true/>
             </div>
         </div>
     </form>