Skip to content

Instantly share code, notes, and snippets.

@mrvicadai
Forked from kspeakman/FileUtils.elm
Created May 7, 2017 19:09
Show Gist options
  • Save mrvicadai/3495cead9ce80bf744af481d7ea6a4ed to your computer and use it in GitHub Desktop.
Save mrvicadai/3495cead9ce80bf744af481d7ea6a4ed to your computer and use it in GitHub Desktop.
Elm file uploads as simple as I could make them

This example uses about 34 lines of code (19 native, 15 Elm) to enable file uploads through normal Elm Http calls (as of 0.18). It's the smallest example I could come up with. No ports, no FileReader. Progress should also work (although not tested yet).

This works by "smuggling" the Javascript File object through Elm as a StringBody. The way Elm handles StringBody is compatible with how you can send a File object via XHR.

This does not currently handle multi selection. Seems like a simple matter to just return a List File and initiate an http send per file. But I haven't needed it yet.

module FileUtils exposing (..)
import Native.FileUtils
import Html exposing (..)
import Html.Events exposing (..)
import Http exposing (Body)
import Json.Decode as Json
type alias File =
{ name : String
, size : Int
, contentType : String
, body : Body
}
onFileChanged : (Maybe File -> msg) -> Attribute msg
onFileChanged fMsg =
on "change" (Json.value |> Json.map (Native.FileUtils.toFile File >> fMsg))
// located at Native/FileUtils.js
// must add "native-modules": true to elm-package.json
// first line must be changed to match author/project, format:
// var _[author]$[project]$Native_FileUtils = function() {
var _local$local$Native_FileUtils = function() {
var convertToFile = function (fValue, file) {
var body = {
ctor : 'StringBody',
_0 : file.type,
_1 : file
};
return A4(fValue, file.name, file.size, file.type, body);
// return fValue(file.name)(file.size)(file.type)(body);
};
var toFile = function(fValue, e) {
if (e && e.target && e.target.files && e.target.files[0])
return _elm_lang$core$Maybe$Just(convertToFile(fValue, e.target.files[0]));
else
return _elm_lang$core$Maybe$Nothing;
};
return {
toFile : F2(toFile)
};
}();
-- this is not runnable by itself
-- it is only meant to demonstrate the necessary bits
open FileUtils exposing (File, onFileChanged)
type Msg
= FileUploadRequested (Maybe File)
| FileUploadReturned (Result Http.Error ())
uploadRequest : String -> File -> HttpRequest ()
uploadRequest url file =
Http.request
{ method = "POST"
, headers = []
, url = url ++ "/" ++ Http.uriEncode file.name
, body = file.body
, expect = Http.expectStringResponse (always <| Ok ())
, timeout = Nothing
, withCredentials = False
}
update : Model -> Msg -> (Model, Cmd Msg)
update model msg =
case msg of
FileUploadRequested Nothing ->
-- ignore
model ! []
FileUploadRequested (Just file) ->
-- TODO set loading state
model
! [ uploadRequest "http://upload.com" file |> Http.send FileUploadReturned ]
FileUploadReturned (Err err) ->
-- TODO set error message
model ! []
FileUploadReturned (Ok ()) ->
-- TODO set success message
model ! []
view : Model -> Html Msg
view model =
input [ type_ "file", onFileChanged FileUploadRequested ] []
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment