UI: js file upload work

This commit is contained in:
Brad Fitzpatrick 2011-06-09 15:00:20 -07:00
parent 3b3b682c1e
commit 0ada583435
6 changed files with 208 additions and 68 deletions

View File

@ -45,14 +45,21 @@ func ReturnJson(conn http.ResponseWriter, data interface{}) {
conn.Header().Set("Content-Type", "text/javascript")
if m, ok := data.(map[string]interface{}); ok {
statusCode := 0
if t, ok := m["error"].(string); ok && len(t) > 0 {
statusCode = http.StatusInternalServerError
}
if t, ok := m["errorType"].(string); ok {
switch t {
case "server":
conn.WriteHeader(http.StatusInternalServerError)
statusCode = http.StatusInternalServerError
case "input":
conn.WriteHeader(http.StatusBadRequest)
statusCode = http.StatusBadRequest
}
}
if statusCode != 0 {
conn.WriteHeader(statusCode)
}
}
bytes, err := json.MarshalIndent(data, "", " ")

View File

@ -17,7 +17,6 @@ limitations under the License.
package main
import (
"bytes"
"fmt"
"http"
"io"
@ -207,48 +206,6 @@ func (ui *UIHandler) serveDiscovery(rw http.ResponseWriter, req *http.Request) {
}
}
func (ui *UIHandler) serveUploadHelper(rw http.ResponseWriter, req *http.Request) {
if ui.Storage == nil {
http.Error(rw, "No BlobRoot configured", 500)
return
}
var buf bytes.Buffer
defer io.Copy(rw, &buf)
fmt.Fprintf(&buf, "<pre>\n")
mr, err := req.MultipartReader()
if err != nil {
fmt.Fprintf(&buf, "multipart reader: %v", err)
return
}
for {
part, err := mr.NextPart()
if err == os.EOF {
break
}
if err != nil {
buf.Reset()
http.Error(rw, "Multipart error: "+err.String(), 500)
break
}
fileName := part.FileName()
if fileName == "" {
continue
}
get, _ := http.ParseQuery(req.URL.RawQuery)
writeFn := schema.WriteFileFromReader
if len(get["rollsum"]) == 1 && get["rollsum"][0] == "1" {
writeFn = schema.WriteFileFromReaderRolling
}
br, err := writeFn(ui.Storage, fileName, part)
fmt.Fprintf(&buf, "filename=%q, formname=%q, br=<a href='./?b=%s'>%s</a>, err=%v\n", part.FileName(), part.FormName(), br, br, err)
}
}
func (ui *UIHandler) storageSeekFetcher() (blobref.SeekFetcher, os.Error) {
return blobref.SeekerFromStreamingFetcher(ui.Storage)
}

View File

@ -63,7 +63,7 @@ function camliSigDiscovery(opts) {
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) { return; }
if (xhr.status != 200) {
opts.fail("no status 200; got " + xhr.status);
opts.fail("camliSigDiscovery expected status 200; got " + xhr.status);
return;
}
sigdisco = JSON.parse(xhr.responseText);
@ -171,6 +171,41 @@ function camliSign(clearObj, opts) {
});
}
function camliUploadFileHelper(file, opts) {
opts = saneOpts(opts);
if (!disco.uploadHelper) {
opts.fail("no uploadHelper available");
return
}
var fd = new FormData();
fd.append(fd, file);
var xhr = new XMLHttpRequest();
xhr.open("POST", disco.uploadHelper);
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) {
return;
}
if (xhr.status != 200) {
opts.fail("got status " + xhr.status);
return;
}
var resj;
try {
resj = JSON.parse(xhr.responseText);
} catch (x) {
opts.fail("error parsing JSON in upload response: " + xhr.responseText);
return;
}
if (resj.error) {
opts.fail("error uploading " + blobref + ": " + resj.error);
return;
}
opts.success(resj);
};
xhr.send(fd);
}
function camliUploadString(s, opts) {
opts = saneOpts(opts);
var blobref = "sha1-" + Crypto.SHA1(s);
@ -205,7 +240,7 @@ function camliUploadString(s, opts) {
opts.fail("error parsing JSON in upload response: " + xhr.responseText);
return;
}
if (resj.errorText) {
if (resj.errorText) { // TODO: change this to error, not errorText, to be consistent
opts.fail("error uploading " + blobref + ": " + resj.errorText);
return;
}
@ -257,14 +292,14 @@ function camliGetRecentlyUpdatedPermanodes(opts) {
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) { return; }
if (xhr.status != 200) {
opts.fail("no status 200; got " + xhr.status);
opts.fail("camliGetRecentlyUpdatedPermanodes expected status 200; got " + xhr.status);
return;
}
var resj;
try {
resj = JSON.parse(xhr.responseText);
} catch(x) {
opts.fail("error parsing JSON in upload response: " + xhr.responseText);
opts.fail("error parsing JSON in response: " + xhr.responseText);
return
}
opts.success(resj);
@ -273,6 +308,33 @@ function camliGetRecentlyUpdatedPermanodes(opts) {
xhr.send();
}
function camliFindExistingFileSchemas(bytesRef, opts) {
opts = saneOpts(opts);
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) { return; }
if (xhr.status != 200) {
opts.fail("camliFindExistingFileSchemas expected status 200; got " + xhr.status + ", " + xhr.statusText);
return;
}
var resj;
try {
resj = JSON.parse(xhr.responseText);
} catch(x) {
opts.fail("error parsing JSON in response: " + xhr.responseText);
return
}
if (resj.error) {
opts.fail(resj.error);
} else {
opts.success(resj);
}
};
var path = disco.searchRoot + "camli/search/files?bytesref=" + bytesRef;
xhr.open("GET", path, true);
xhr.send();
}
// Returns true if the passed-in string might be a blobref.
function isPlausibleBlobRef(blobRef) {
return /^\w+-[a-f0-9]+$/.test(blobRef);

View File

@ -7,6 +7,13 @@
<script type="text/javascript" src="camli.js"></script>
<script type="text/javascript" src="?camli.mode=config&cb=onConfiguration"></script>
<script type="text/javascript" src="permanode.js"></script>
<style>
div.fileupload {
margin: 0.25em;
border: 1px solid #888;
padding: 0.25em;
}
</style>
</head>
<body>
<div>[<a href="./">Home</a>]</div>

View File

@ -130,28 +130,55 @@ var lastFiles;
function handleFiles(files) {
lastFiles = files;
info = document.getElementById("info");
t = "N files: " + files.length + "\n";
for (var i = 0; i < files.length; i++) {
var file = files[i];
t += "file[" + i + "] name=" + file.name + "; size=" + file.size + "; file.type=" + file.type + "\n";
(function(file) {
var fr = new FileReader();
fr.onload = function() {
dataurl = fr.result;
comma = dataurl.indexOf(",")
if (comma != -1) {
b64 = dataurl.substring(comma + 1);
var arrayBuffer = Base64.decode(b64).buffer;
var hash = Crypto.SHA1(new Uint8Array(arrayBuffer, 0));
info.innerHTML += "File " + file.name + " = sha1-" + hash + "\n";
}
};
fr.readAsDataURL(file);
})(file);
startFileUpload(file);
}
info.innerHTML += t;
}
function startFileUpload(file) {
var dnd = document.getElementById("dnd");
var up = document.createElement("div");
up.setAttribute("class", "fileupload");
dnd.appendChild(up);
var info = "name=" + file.name + " size=" + file.size + "; type=" + file.type;
up.innerHTML = info + " (scanning)";
var contentsRef; // set later
var onFail = function(msg) {
up.innerHTML = info + " <b>fail:</b> ";
up.appendChild(document.createTextNode(msg));
};
var onUploaded = function(res) {
alert("Uploaded: " + JSON.stringify(res, null, 2));
};
var onFileSearch = function(res) {
if (res.files.length > 0) {
up.innerHTML = info + " <b>TODO: server dup, handle</b>";
alert("TODO: server already has it, maybe. verify the files in " + JSON.stringify(res, null, 2));
return;
}
up.innerHTML = info + " <b>Uploading...</b>";
camliUploadFileHelper(file, { success: onUploaded, fail: onFail });
};
var fr = new FileReader();
fr.onload = function() {
dataurl = fr.result;
comma = dataurl.indexOf(",")
if (comma != -1) {
b64 = dataurl.substring(comma + 1);
var arrayBuffer = Base64.decode(b64).buffer;
var hash = Crypto.SHA1(new Uint8Array(arrayBuffer, 0));
contentsRef = "sha1-" + hash;
up.innerHTML = info + " (checking for dup of " + contentsRef + ")";
camliFindExistingFileSchemas(contentsRef, { success: onFileSearch, fail: onFail });
}
};
fr.readAsDataURL(file);
}
function onFileFormSubmit(e) {

View File

@ -0,0 +1,80 @@
/*
Copyright 2011 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"http"
"os"
"camli/httputil"
"camli/schema"
)
func (ui *UIHandler) serveUploadHelper(rw http.ResponseWriter, req *http.Request) {
rollSum := req.URL.Query().Get("rollsum") == "1"
ret := make(map[string]interface{})
defer httputil.ReturnJson(rw, ret)
if ui.Storage == nil {
ret["error"] = "No BlobRoot configured"
ret["errorType"] = "server"
return
}
mr, err := req.MultipartReader()
if err != nil {
ret["error"] = "reading body: " + err.String()
ret["errorType"] = "server"
return
}
got := make([]map[string]interface{}, 0)
for {
part, err := mr.NextPart()
if err == os.EOF {
break
}
if err != nil {
ret["error"] = "reading body: " + err.String()
ret["errorType"] = "server"
break
}
fileName := part.FileName()
if fileName == "" {
continue
}
writeFn := schema.WriteFileFromReader
if rollSum {
writeFn = schema.WriteFileFromReaderRolling
}
br, err := writeFn(ui.Storage, fileName, part)
if err == nil {
got = append(got, map[string]interface{}{
"filename": part.FileName(),
"formname": part.FormName(),
"fileref": br.String(),
})
} else {
ret["error"] = "writing to blobserver: " + err.String()
return
}
}
ret["got"] = got
}