diff --git a/src/main/java/it/inaf/ia2/transfer/controller/PutFileController.java b/src/main/java/it/inaf/ia2/transfer/controller/PutFileController.java
index 2ced2a915ec557c8f8ca9d4b888916f034585c16..fcc19d6ba613e21b818d626c61492b14c2a314a7 100644
--- a/src/main/java/it/inaf/ia2/transfer/controller/PutFileController.java
+++ b/src/main/java/it/inaf/ia2/transfer/controller/PutFileController.java
@@ -6,6 +6,7 @@
 package it.inaf.ia2.transfer.controller;
 
 import it.inaf.ia2.transfer.exception.FileNotFoundException;
+import it.inaf.ia2.transfer.exception.InsufficientStorageException;
 import it.inaf.ia2.transfer.exception.InvalidArgumentException;
 import it.inaf.ia2.transfer.persistence.model.FileInfo;
 import it.inaf.ia2.transfer.persistence.FileDAO;
@@ -64,14 +65,24 @@ public class PutFileController extends FileController {
             Optional<FileInfo> optFileInfo = fileDAO.getFileInfo(path);
 
             if (optFileInfo.isPresent()) {
-                try (InputStream in = file != null ? file.getInputStream() : request.getInputStream()) {
-                    FileInfo fileInfo = optFileInfo.get();
+                FileInfo fileInfo = optFileInfo.get();
+
+                String parentPath = fileInfo.getVirtualPath().substring(0, fileInfo.getVirtualPath().lastIndexOf("/"));
+                Long remainingQuota = fileDAO.getRemainingQuota(parentPath);
+
+                // if MultipartFile provides file size it is possible to check
+                // quota limit before reading the stream
+                if (remainingQuota != null && file != null && file.getSize() > remainingQuota) {
+                    throw new InsufficientStorageException(fileInfo.getVirtualPath());
+                }
+                
+                if (file != null) {
+                    fileInfo.setContentType(file.getContentType());
+                }
+                fileInfo.setContentEncoding(contentEncoding);
 
-                    if (file != null) {
-                        fileInfo.setContentType(file.getContentType());
-                    }
-                    fileInfo.setContentEncoding(contentEncoding);
-                    storeGenericFile(fileInfo, in, jobId);
+                try (InputStream in = file != null ? file.getInputStream() : request.getInputStream()) {
+                    storeGenericFile(fileInfo, in, jobId, remainingQuota);
                 } catch (IOException | NoSuchAlgorithmException ex) {
                     throw new RuntimeException(ex);
                 }
@@ -81,7 +92,7 @@ public class PutFileController extends FileController {
         }, jobId);
     }
 
-    private void storeGenericFile(FileInfo fileInfo, InputStream is, String jobId) throws IOException, NoSuchAlgorithmException {
+    private void storeGenericFile(FileInfo fileInfo, InputStream is, String jobId, Long remainingQuota) throws IOException, NoSuchAlgorithmException {
 
         File file = new File(fileInfo.getOsPath());
 
@@ -112,6 +123,13 @@ public class PutFileController extends FileController {
             }
 
             Long fileSize = Files.size(file.toPath());
+
+            // Quota limit is checked again to handle cases where MultipartFile is not used
+            if (remainingQuota != null && fileSize > remainingQuota) {
+                file.delete();
+                throw new InsufficientStorageException(fileInfo.getVirtualPath());
+            }
+
             String md5Checksum = makeMD5Checksum(file);
 
             fileDAO.updateFileAttributes(fileInfo.getNodeId(),
diff --git a/src/main/java/it/inaf/ia2/transfer/exception/InsufficientStorageException.java b/src/main/java/it/inaf/ia2/transfer/exception/InsufficientStorageException.java
new file mode 100644
index 0000000000000000000000000000000000000000..9e2de5996e140d7decfce1d4014b7e2c2f27358a
--- /dev/null
+++ b/src/main/java/it/inaf/ia2/transfer/exception/InsufficientStorageException.java
@@ -0,0 +1,18 @@
+/*
+ * This file is part of vospace-file-service
+ * Copyright (C) 2021 Istituto Nazionale di Astrofisica
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+package it.inaf.ia2.transfer.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(HttpStatus.INSUFFICIENT_STORAGE)
+public class InsufficientStorageException extends JobException {
+
+    public InsufficientStorageException(String path) {
+        super(Type.FATAL, "Quota Exceeded");
+        setErrorDetail("QuotaExceeded Path: " + path);
+    }
+}
diff --git a/src/test/java/it/inaf/ia2/transfer/controller/PutFileControllerTest.java b/src/test/java/it/inaf/ia2/transfer/controller/PutFileControllerTest.java
index 9fe0554eb418098a137e90154ea8c54c0fd0557a..72f820bc328b5cacce5e71b70620b3be5c260891 100644
--- a/src/test/java/it/inaf/ia2/transfer/controller/PutFileControllerTest.java
+++ b/src/test/java/it/inaf/ia2/transfer/controller/PutFileControllerTest.java
@@ -5,14 +5,18 @@
  */
 package it.inaf.ia2.transfer.controller;
 
+import it.inaf.ia2.transfer.exception.InsufficientStorageException;
 import it.inaf.ia2.transfer.persistence.model.FileInfo;
 import it.inaf.ia2.transfer.persistence.FileDAO;
 import it.inaf.ia2.transfer.persistence.JobDAO;
+import java.io.ByteArrayInputStream;
 import java.io.File;
+import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.util.Optional;
 import java.util.UUID;
+import javax.servlet.ServletInputStream;
 import net.ivoa.xml.uws.v1.ExecutionPhase;
 import org.assertj.core.util.Files;
 import org.junit.jupiter.api.AfterAll;
@@ -22,6 +26,8 @@ import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
+import org.mockito.Mockito;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -70,6 +76,8 @@ public class PutFileControllerTest {
     @Test
     public void putGenericFile() throws Exception {
 
+        when(fileDao.getRemainingQuota(any())).thenReturn(null);
+        
         String randomFileName = UUID.randomUUID().toString();
         createBaseFileInfo(randomFileName);
 
@@ -99,6 +107,8 @@ public class PutFileControllerTest {
 
     private void putGenericFileWithNameConflict(String name1, String name2, String name3) throws Exception {
 
+        when(fileDao.getRemainingQuota(any())).thenReturn(null);
+        
         createBaseFileInfo(name1);
 
         MockMultipartFile fakeFile = new MockMultipartFile("file", "test.txt", "text/plain", "content".getBytes());
@@ -143,6 +153,8 @@ public class PutFileControllerTest {
     @Test
     public void putGenericFileWithJobId() throws Exception {
 
+        when(fileDao.getRemainingQuota(any())).thenReturn(null);
+        
         when(jobDAO.isJobExisting("pippo10")).thenReturn(false);
         when(jobDAO.isJobExisting("pippo5")).thenReturn(true);
 
@@ -221,19 +233,73 @@ public class PutFileControllerTest {
 
         verify(jobDAO, times(1)).setJobError(eq("abcdef"), any());
     }
+    
+    @Test
+    public void testQuotaExceededMultipart() throws Exception {
+
+        when(fileDao.getRemainingQuota(eq("/path/to"))).thenReturn(0l);
+
+        createBaseFileInfo();
+
+        MockMultipartFile fakeFile = new MockMultipartFile("file", "test.txt", null, "content".getBytes());
+
+        Exception ex = mockMvc.perform(putMultipart("/path/to/test.txt")
+                .file(fakeFile))
+                .andDo(print())
+                .andExpect(status().is5xxServerError())
+                .andReturn().getResolvedException();
+
+        verify(fileDao, times(1)).getRemainingQuota(eq("/path/to"));
+
+        assertTrue(ex instanceof InsufficientStorageException);
+    }
+
+    @Test
+    public void testQuotaExceededStream() throws Exception {
+
+        when(fileDao.getRemainingQuota(eq("/path/to"))).thenReturn(0l);
+
+        createBaseFileInfo();
+
+        MockHttpServletRequestBuilder streamBuilder = put("/path/to/test.txt");
+        streamBuilder.with(new RequestPostProcessor() {
+            @Override
+            public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
+                MockHttpServletRequest spyRequest = spy(request);
+                ByteArrayInputStream bais = new ByteArrayInputStream("some data".getBytes());
+                ServletInputStream sis = mock(ServletInputStream.class);
+                try {
+                    when(sis.transferTo(any())).thenAnswer(i -> bais.transferTo(i.getArgument(0)));
+                } catch (IOException ex) {
+                }
+                Mockito.doReturn(sis).when(spyRequest).getInputStream();
+                return spyRequest;
+            }
+        });
+
+        Exception ex = mockMvc.perform(streamBuilder)
+                .andDo(print())
+                .andExpect(status().is5xxServerError())
+                .andReturn().getResolvedException();
+
+        verify(fileDao, times(1)).getRemainingQuota(eq("/path/to"));
+
+        assertTrue(ex instanceof InsufficientStorageException);
+    }
 
     private FileInfo createBaseFileInfo() {
         String randomFileName = UUID.randomUUID().toString();
         return createBaseFileInfo(randomFileName);
     }
-
-    private FileInfo createBaseFileInfo(String fileName) {
+    
+    private FileInfo createBaseFileInfo(String fileName) {        
         FileInfo fileInfo = new FileInfo();
         fileInfo.setOsPath(getTestFilePath(fileName));
+        fileInfo.setVirtualPath("/path/to/" + fileName);
         fileInfo.setPublic(false);
-
+        
         when(fileDao.getFileInfo(any())).thenReturn(Optional.of(fileInfo));
-
+        
         return fileInfo;
     }