diff --git a/core/lib/Drupal/Component/Utility/BytesValidator.php b/core/lib/Drupal/Component/Utility/BytesValidator.php
new file mode 100644
index 0000000000..831c42213e
--- /dev/null
+++ b/core/lib/Drupal/Component/Utility/BytesValidator.php
@@ -0,0 +1,150 @@
+<?php
+
+namespace Drupal\Component\Utility;
+
+/**
+ * Provides helper method for byte size string validation.
+ *
+ * @ingroup utility
+ */
+class BytesValidator {
+
+  /**
+   * Validates a bytes size string is valid and can be parsed by Bytes:toInt().
+   *
+   * @param string $byte_size_string
+   *   A user entered string representing a byte size to be validated.
+   *
+   * @return bool
+   *   TRUE if meaningful byte size string, FALSE if not.
+   */
+  public static function validateByteSizeString($byte_size_string) {
+
+    // Spaces are irrelevant to us. Bytes::toInt will process with or without
+    // them. Make our lives easier by just doing away with them.
+    $byte_size_string = self::stripWhitespaceFromString($byte_size_string);
+
+    // Having removed whitespace, if our string is now empty then the user
+    // either entered nothing or a string of whitespace. Accept both cases as
+    // valid.
+    if (empty($byte_size_string)) {
+      return TRUE;
+    }
+
+    // Theoretically self::stringIsValidByteString() should perform strict
+    // enough validation. Passing the string to Bytes::toInt() for added
+    // precaution.
+    if (!self::sizeIsValidByteString($byte_size_string) || !self::toIntCanParseSize($byte_size_string)) {
+      return FALSE;
+    }
+
+    return TRUE;
+  }
+
+  /**
+   * Strips whitespace from a string of text.
+   *
+   * @param string $byte_size_string
+   *   Holds a string representing a byte size to be validated.
+   *
+   *   A string size expressed as a number of bytes with optional SI
+   *   or IEC binary unit prefix (e.g. 2, 3K, 5MB, 10G, 6GiB, 8 bytes, 9mbytes).
+   *
+   * @return string
+   *   Returns the inputted string with spaces removed.
+   */
+  private static function stripWhitespaceFromString($byte_size_string) {
+    return trim(preg_replace('/\s+/', '', $byte_size_string));
+  }
+
+  /**
+   * Validates the given string looks like a byte size string.
+   *
+   * @param string $byte_size_string
+   *   Holds a string representing a byte size to be validated.
+   *
+   *   A string size expressed as a number of bytes with optional SI
+   *   or IEC binary unit prefix (e.g. 2, 3K, 5MB, 10G, 6GiB, 8 bytes, 9mbytes).
+   *
+   * @return bool
+   *   TRUE if size is a valid byte size string, FALSE if not.
+   */
+  private static function sizeIsValidByteString($byte_size_string) {
+    return !(
+      self::sizeIsNotAlphaNumeric($byte_size_string) ||
+      self::sizeNotStartWithNumber($byte_size_string) ||
+      self::sizeHasNumbersFollowingAlphaChar($byte_size_string)
+    );
+  }
+
+  /**
+   * Determines that a string does not start with a decimal character.
+   *
+   * @param string $byte_size_string
+   *   Holds a string representing a byte size to be validated.
+   *
+   *   A string size expressed as a number of bytes with optional SI
+   *   or IEC binary unit prefix (e.g. 2, 3K, 5MB, 10G, 6GiB, 8 bytes, 9mbytes).
+   *
+   * @return bool
+   *   TRUE if the string does not start with a numeric char, FALSE otherwise.
+   */
+  private static function sizeNotStartWithNumber($byte_size_string) {
+    return !preg_match('/^\d/', $byte_size_string);
+  }
+
+  /**
+   * Determines if a string has decimal chars following after alpha chars.
+   *
+   * @param string $byte_size_string
+   *   Holds a string representing a byte size to be validated.
+   *
+   *   A string size expressed as a number of bytes with optional SI
+   *   or IEC binary unit prefix (e.g. 2, 3K, 5MB, 10G, 6GiB, 8 bytes, 9mbytes).
+   *
+   * @return bool
+   *   TRUE if the string has decimal values following after alpha chars.
+   */
+  private static function sizeHasNumbersFollowingAlphaChar($byte_size_string) {
+    return (bool) preg_match('/[a-zA-Z]+(\d+)/', $byte_size_string);
+  }
+
+  /**
+   * Determines if a string contains non alphanumeric characters.
+   *
+   * @param string $byte_size_string
+   *   Holds a string representing a byte size to be validated.
+   *
+   *   A string size expressed as a number of bytes with optional SI
+   *   or IEC binary unit prefix (e.g. 2, 3K, 5MB, 10G, 6GiB, 8 bytes, 9mbytes).
+   *
+   * @return bool
+   *   Returns TRUE if non alphanumeric characters are found.
+   */
+  private static function sizeIsNotAlphaNumeric($byte_size_string) {
+    return (bool) !preg_match('/^[a-zA-Z0-9]+$/', $byte_size_string);
+  }
+
+  /**
+   * Determines if Bytes:toInt() is able to parse the given string.
+   *
+   * @param string $byte_size_string
+   *   Holds a string representing a byte size to be validated.
+   *
+   *   A string size expressed as a number of bytes with optional SI
+   *   or IEC binary unit prefix (e.g. 2, 3K, 5MB, 10G, 6GiB, 8 bytes, 9mbytes).
+   *
+   * @return bool
+   *   TRUE if string can be parsed by Bytes::toInt(), FALSE otherwise.
+   */
+  private static function toIntCanParseSize($byte_size_string) {
+    try {
+      Bytes::toInt($byte_size_string);
+    }
+    catch (\Exception $e) {
+      return FALSE;
+    }
+    return TRUE;
+  }
+
+}
diff --git a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
index 8dd882de02..0b5d5b4712 100644
--- a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
+++ b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
@@ -4,6 +4,7 @@
 
 use Drupal\Component\Utility\Bytes;
 use Drupal\Component\Render\PlainTextOutput;
+use Drupal\Component\Utility\BytesValidator;
 use Drupal\Component\Utility\Environment;
 use Drupal\Component\Utility\Random;
 use Drupal\Core\Field\FieldDefinitionInterface;
@@ -12,6 +13,7 @@
 use Drupal\Core\File\FileSystemInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\StreamWrapper\StreamWrapperInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\DataDefinition;
 
 /**
@@ -181,7 +183,7 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
       '#default_value' => $settings['max_filesize'],
       '#description' => t('Enter a value like "512" (bytes), "80 KB" (kilobytes) or "50 MB" (megabytes) in order to restrict the allowed file size. If left empty the file sizes will be limited only by PHP\'s maximum post and file upload sizes (current limit <strong>%limit</strong>).', ['%limit' => format_size(Environment::getUploadMaxSize())]),
       '#size' => 10,
-      '#element_validate' => [[get_class($this), 'validateMaxFilesize']],
+      '#element_validate' => [[get_class($this), 'validateExtensions']],
       '#weight' => 5,
     ];
 
@@ -197,11 +199,11 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
   }
 
   /**
-   * Form API callback
+   * Form API callback.
    *
    * Removes slashes from the beginning and end of the destination value and
-   * ensures that the file directory path is not included at the beginning of the
-   * value.
+   * ensures that the file directory path is not included at the beginning of
+   * the value.
    *
    * This function is assigned as an #element_validate callback in
    * fieldSettingsForm().
@@ -220,7 +222,8 @@ public static function validateDirectory($element, FormStateInterface $form_stat
    *
    * This doubles as a convenience clean-up function and a validation routine.
    * Commas are allowed by the end-user, but ultimately the value will be stored
-   * as a space-separated list for compatibility with file_validate_extensions().
+   * as a space-separated list for compatibility with
+   * file_validate_extensions().
    */
   public static function validateExtensions($element, FormStateInterface $form_state) {
     if (!empty($element['#value'])) {
@@ -239,15 +242,17 @@ public static function validateExtensions($element, FormStateInterface $form_sta
   /**
    * Form API callback.
    *
-   * Ensures that a size has been entered and that it can be parsed by
+   * Ensures that a valid size has been entered and that it can be parsed by
    * \Drupal\Component\Utility\Bytes::toInt().
    *
    * This function is assigned as an #element_validate callback in
    * fieldSettingsForm().
    */
   public static function validateMaxFilesize($element, FormStateInterface $form_state) {
-    if (!empty($element['#value']) && !is_numeric(Bytes::toInt($element['#value']))) {
-      $form_state->setError($element, t('The "@name" option must contain a valid value. You may either leave the text field empty or enter a string like "512" (bytes), "80 KB" (kilobytes) or "50 MB" (megabytes).', ['@name' => $element['title']]));
+    $bytes_validator = new BytesValidator();
+    if ($bytes_validator->validateByteSizeString($element['#value'])) {
+      $translatable_markup = new TranslatableMarkup('The "@name" option must contain a valid value. You may either leave the text field empty or enter a string like "512" (bytes), "80 KB" (kilobytes) or "50 MB" (megabytes).', ['@name' => $element['#title']]);
+      $form_state->setError($element, $translatable_markup);
     }
   }
 
@@ -260,10 +265,9 @@ public static function validateMaxFilesize($element, FormStateInterface $form_st
    * @return string
    *   An unsanitized file directory URI with tokens replaced. The result of
    *   the token replacement is then converted to plain text and returned.
-   *
    * @see \Drupal\Core\Utility\Token::replace()
    */
-  public function getUploadLocation($data = []) {
+  public function getUploadLocation(array $data = []) {
     return static::doGetUploadLocation($this->getSettings(), $data);
   }
 
@@ -281,7 +285,7 @@ public function getUploadLocation($data = []) {
    *
    * @see \Drupal\Core\Utility\Token::replace()
    */
-  protected static function doGetUploadLocation(array $settings, $data = []) {
+  protected static function doGetUploadLocation(array $settings, array $data = []) {
     $destination = trim($settings['file_directory'], '/');
 
     // Replace tokens. As the tokens might contain HTML we convert it to plain
diff --git a/core/modules/file/tests/src/Unit/BytesValidatorTest.php b/core/modules/file/tests/src/Unit/BytesValidatorTest.php
new file mode 100644
index 0000000000..f03b0c15a0
--- /dev/null
+++ b/core/modules/file/tests/src/Unit/BytesValidatorTest.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace Drupal\Tests\file\Unit;
+
+use Drupal\Component\Utility\BytesValidator;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests FileItem's settings form's validation.
+ *
+ * @group file
+ */
+class BytesValidatorTest extends UnitTestCase {
+
+  /**
+   * Tests that a sane Max file size is entered.
+   *
+   * @dataProvider maxFileSizeInputsProvider
+   */
+  public function testValidateByteSizeString($input, $expected) {
+    $result = BytesValidator::validateByteSizeString($input);
+    $this->assertEquals($expected, $result);
+  }
+
+  /**
+   * Data provider for testValidateMaxfilesize().
+   *
+   * @return array[]
+   *   Array of inputs and error expectations.
+   */
+  public function maxFileSizeInputsProvider() {
+    $data = [
+      ['0 bytes', TRUE],
+      ['   ', TRUE],
+      ['', TRUE],
+      ['0 K', TRUE],
+      ['100bytes', TRUE],
+      ['10000', TRUE],
+      ['100 bytes', TRUE],
+      ['999989 bytes', TRUE],
+      ['999989bytes', TRUE],
+      ['2', TRUE],
+      ['3K', TRUE],
+      ['5MB', TRUE],
+      ['10G', TRUE],
+      ['6GiB', TRUE],
+      ['8bytes', TRUE],
+      ['9mbytes', TRUE],
+      // Should we allow these spaces?
+      ['1 0 0 0 0 m b y tes', TRUE],
+      [' 123456 ', TRUE],
+
+      ['123456.03 ', FALSE],
+      ['nonumbers', FALSE],
+      ['bread', FALSE],
+      ['bananas', FALSE],
+      ['1234b1', FALSE],
+      ['543xd1', FALSE],
+      ['-1', FALSE],
+      ['-975', FALSE],
+      [' word ', FALSE],
+      [' word', FALSE],
+      ['word ', FALSE],
+      [' words with spaces ', FALSE],
+      ['words with spaces ', FALSE],
+      [' words with spaces', FALSE],
+      [' 123456.03 ', FALSE],
+      ['123456.03 ', FALSE],
+      [' 123456.03', FALSE],
+      [' some words & stuff', FALSE],
+    ];
+
+    // Improve test output by using the input value as a label for the dataset.
+    foreach ($data as $index => $item) {
+      // If the index looks like an in eg "1000" "2" etc, php still detects
+      // it as a numeric index. Adding text to all the labels to force them
+      // to render.
+      $data["Sample input: '{$item[0]}'"] = $item;
+      unset($data[$index]);
+    }
+
+    return $data;
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Component/Utility/BytesTest.php b/core/tests/Drupal/Tests/Component/Utility/BytesTest.php
deleted file mode 100644
index 72485ea802..0000000000
--- a/core/tests/Drupal/Tests/Component/Utility/BytesTest.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-namespace Drupal\Tests\Component\Utility;
-
-use Drupal\Component\Utility\Bytes;
-use PHPUnit\Framework\TestCase;
-
-/**
- * Tests bytes size parsing helper methods.
- *
- * @group Utility
- *
- * @coversDefaultClass \Drupal\Component\Utility\Bytes
- */
-class BytesTest extends TestCase {
-
-  /**
-   * Tests \Drupal\Component\Utility\Bytes::toInt().
-   *
-   * @param int $size
-   *   The value for the size argument for
-   *   \Drupal\Component\Utility\Bytes::toInt().
-   * @param int $expected_int
-   *   The expected return value from
-   *   \Drupal\Component\Utility\Bytes::toInt().
-   *
-   * @dataProvider providerTestToInt
-   * @covers ::toInt
-   */
-  public function testToInt($size, $expected_int) {
-    $this->assertEquals($expected_int, Bytes::toInt($size));
-  }
-
-  /**
-   * Provides data for testToInt.
-   *
-   * @return array
-   *   An array of arrays, each containing the argument for
-   *   \Drupal\Component\Utility\Bytes::toInt(): size, and the expected return
-   *   value.
-   */
-  public function providerTestToInt() {
-    return [
-      ['1', 1],
-      ['1 byte', 1],
-      ['1 KB'  , Bytes::KILOBYTE],
-      ['1 MB'  , pow(Bytes::KILOBYTE, 2)],
-      ['1 GB'  , pow(Bytes::KILOBYTE, 3)],
-      ['1 TB'  , pow(Bytes::KILOBYTE, 4)],
-      ['1 PB'  , pow(Bytes::KILOBYTE, 5)],
-      ['1 EB'  , pow(Bytes::KILOBYTE, 6)],
-      ['1 ZB'  , pow(Bytes::KILOBYTE, 7)],
-      ['1 YB'  , pow(Bytes::KILOBYTE, 8)],
-      ['23476892 bytes', 23476892],
-      // 76 MB.
-      ['76MRandomStringThatShouldBeIgnoredByParseSize.', 79691776],
-      // 76.24 GB (with typo).
-      ['76.24 Giggabyte', 81862076662],
-    ];
-  }
-
-}
