mirror of https://github.com/perkeep/perkeep.git
UI: js file upload work
This commit is contained in:
parent
3b3b682c1e
commit
0ada583435
|
@ -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, "", " ")
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue