diff --git a/Data/Conduit/Text.hs b/Data/Conduit/Text.hs
--- a/Data/Conduit/Text.hs
+++ b/Data/Conduit/Text.hs
@@ -21,6 +21,7 @@
     , ascii
     , iso8859_1
     , lines
+    , linesBounded
     , TextException (..)
     ) where
 
@@ -43,7 +44,7 @@
 import Data.Conduit
 import qualified Data.Conduit.List as CL
 import Control.Monad.Trans.Class (lift)
-import Control.Monad (unless)
+import Control.Monad (unless,when)
 
 -- | A specific character encoding.
 --
@@ -64,6 +65,9 @@
     showsPrec d c = showParen (d > 10) $
         showString "Codec " . shows (codecName c)
 
+
+
+
 -- | Emit each line separately
 --
 -- Since 0.4.1
@@ -86,6 +90,46 @@
       where
         (first', second) = T.break (== '\n') more
 
+
+
+-- | Variant of the lines function with an integer parameter.
+-- The text length of any emitted line
+-- never exceeds the value of the paramater. Whenever
+-- this is about to happen a LengthExceeded exception
+-- is thrown. This function should be used instead
+-- of the lines function whenever we are dealing with
+-- user input (e.g. a file upload) because we can't be sure that
+-- user input won't have extraordinarily large lines which would
+-- require large amounts of memory if consumed.
+linesBounded :: MonadThrow m => Int -> Conduit T.Text m T.Text
+linesBounded maxLineLen =
+    loop 0 id
+  where
+    loop len front = await >>= maybe (finish front) (go len front)
+
+    finish front =
+        let final = front T.empty
+         in unless (T.null final) (yield final)
+    go len sofar more =
+        case T.uncons second of
+            Just (_, second') -> do
+                let toYield = sofar first'
+                    len' = len + T.length first'
+                when (len' > maxLineLen)
+                    (lift $ monadThrow (LengthExceeded maxLineLen))
+                yield toYield
+                go 0 id second'
+            Nothing -> do
+                let len' = len + T.length more
+                when (len' > maxLineLen) $
+                    (lift $ monadThrow (LengthExceeded maxLineLen))
+                let rest = sofar more
+                loop len' $ T.append rest
+      where
+        (first', second) = T.break (== '\n') more
+
+
+
 -- | Convert text into bytes, using the provided codec. If the codec is
 -- not capable of representing an input character, an exception will be thrown.
 --
@@ -123,6 +167,7 @@
 -- Since 0.3.0
 data TextException = DecodeException Codec Word8
                    | EncodeException Codec Char
+                   | LengthExceeded Int
                    | TextException Exc.SomeException
     deriving (Show, Typeable)
 instance Exc.Exception TextException
diff --git a/conduit.cabal b/conduit.cabal
--- a/conduit.cabal
+++ b/conduit.cabal
@@ -1,5 +1,5 @@
 Name:                conduit
-Version:             1.0.1
+Version:             1.0.2
 Synopsis:            Streaming data processing library.
 Description:
     @conduit@ is a solution to the streaming data problem, allowing for production, transformation, and consumption of streams of data in constant memory. It is an alternative to lazy I\/O which guarantees deterministic resource handling, and fits in the same general solution space as @enumerator@\/@iteratee@ and @pipes@. For a tutorial, please visit <https://haskell.fpcomplete.com/user/snoyberg/library-documentation/conduit-overview>.
diff --git a/test/main.hs b/test/main.hs
--- a/test/main.hs
+++ b/test/main.hs
@@ -401,6 +401,26 @@
             x <- CL.sourceList ["foobarbaz", error "ignore me"] C.$$ CT.decode CT.utf8 C.=$ CL.head
             x `shouldBe` Just "foobarbaz"
 
+    describe "text lines bounded" $ do
+        it "works across split lines" $
+            (CL.sourceList [T.pack "abc", T.pack "d\nef"] C.$= CT.linesBounded 80 C.$$ CL.consume) ==
+                [[T.pack "abcd", T.pack "ef"]]
+        it "works with multiple lines in an item" $
+            (CL.sourceList [T.pack "ab\ncd\ne"] C.$= CT.linesBounded 80 C.$$ CL.consume) ==
+                [[T.pack "ab", T.pack "cd", T.pack "e"]]
+        it "works with ending on a newline" $
+            (CL.sourceList [T.pack "ab\n"] C.$= CT.linesBounded 80 C.$$ CL.consume) ==
+                [[T.pack "ab"]]
+        it "works with ending a middle item on a newline" $
+            (CL.sourceList [T.pack "ab\n", T.pack "cd\ne"] C.$= CT.linesBounded 80 C.$$ CL.consume) ==
+                [[T.pack "ab", T.pack "cd", T.pack "e"]]
+        it "is not too eager" $ do
+            x <- CL.sourceList ["foobarbaz", error "ignore me"] C.$$ CT.decode CT.utf8 C.=$ CL.head
+            x `shouldBe` Just "foobarbaz"
+        it "throws an exception when lines are too long" $ do
+            x <- C.runExceptionT $ CL.sourceList ["hello\nworld"] C.$$ CT.linesBounded 4 C.=$ CL.consume
+            show x `shouldBe` show (Left $ CT.LengthExceeded 4 :: Either CT.TextException ())
+
     describe "binary isolate" $ do
         it "works" $ do
             bss <- runResourceT $ CL.sourceList (replicate 1000 "X")
