Upload password protected file to server - javascript

I am running a Python HTTP server that host a captive portal. Basically I'm trying to upload a password protected file to the server.
Right now I am able to upload files to the server with JavaScript and FileReader. This is how I do it:
var file_cli_cert = document.getElementById(id="exa_cli_cert").files[0];
const xmlhttp1 = new XMLHttpRequest();
let name = file_cert.name;
let reader = new FileReader();
reader.readAsText(file_cert);
xmlhttp1.open("POST", '/load/cert');
reader.onload = function(){
xmlhttp1.send(name +"&CERT="+reader.result);
With non-password protected files this works well.
For password protected files my idea is to get the file and the password to access to the data. The problem is that I don't know how to access to password protected files in JS and i think it's not possible. So right now i am wondering how to send the file and the password to the server and access the file data there.
If I send the file object with XMLHttpRequest.send(), on the server side I get the object in string format.
To read the POST message, in the server side, I do:
ctype, pdict = cgi.parse_header(self.headers['content-type'])
content_len = int(self.headers.get('Content-length'))
post_body = self.rfile.read(content_len) #read credentials
self.send_response(201)
self.end_headers()
if self.path.endswith('/load/cert'): #if user loads a certificate
post_body = post_body.decode()
post_body = post_body.split("&CERT=") #split name and file content
name_cert = post_body[0]
file_content = post_body[1]
f = open("./certs/"+name_cert, "w")
f.write(file_content)
f.close()
I'm a newbie at this and I have been looking for a solution for hours. Any help will be appreciated.

No python expert but reading a encrypted file as Text with FileReader could be problematic as it could lead to some data loss when encoding it as text. You should rather be reading it as binary using reader.readAsArrayBuffer()
But there is no need to read the content of the file into memory and allocate memory, just upload the blob/file directly to the server and it will take care of sending the data from the disc to the network without touching the main javascript thread without any text transformation.
const [ file ] = document.querySelector('#exa_cli_cert').files
fetch('/load/cert', {
method: 'POST',
body: file,
headers: {
'x-file-name': file.name
}
})
.then(r => r.arrayBuffer())

Related

File not opening after download: ReactJs + Spring Boot

I am developing a web application and one of my use cases is for users to have the ability to upload and download files from the server. I'm using ReactJs and Spring Boot.
Front-End code:
downloadFileClicked(id, fileName, fileType){
TestDataService.downloadFile(id)
.then(response=>{
console.log(response)
const file = new Blob(
[response.data],
{type: fileType}
)
let url = window.URL.createObjectURL(file)
let a = document.createElement('a')
a.href=url
a.download= fileName
a.click()
})
}
Back-End code:
#GetMapping("/downloadFile/{fileId:.+}")
public ResponseEntity<Resource> downloadFile(#PathVariable Long fileId, HttpServletRequest request ){
//Load file as a resource
DatabaseFile databaseFile = fileStorageService.getFile(fileId);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(databaseFile.getFileType()))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\""+databaseFile.getFileName()+"\"")
.body(new ByteArrayResource(databaseFile.getData()));
}
When the user clicks download - the file does indeed download and seemingly in the correct format but when each application tries to open the file I am faced with such errors as the file format is not supported or the file is corrupt.
The file data is being stored as a byte array in a MySQL database. I have tried a number of different methods to download the file but I have encountered the same error each time. Any help would be greatly appreciated!

How to write a file sent via websocket?

I'm making a server sistem in which the server uploads files to a client via websockets. The server is corretly sending the file, but I don't know how to write it in the client side.
I've tried receiving the file data using "msg", "msg.data" and many other ways, but it always result in a 1kb file. I also tried converting the file to base64 before sending and converting back in the client side(so the message would be text instead of a binary), but didn't work.
Server:
var ws = require("nodejs-websocket")
ws.setBinaryFragmentation(99999999999)
var fs = require('fs'),
file = process.argv[2],
data = fs.readFileSync("./map.zip");
var server = ws.createServer(function (connection) {
connection.sendBinary(data)
}).listen(1000)
Client:
var connection = new WebSocket("ws://localhost:1000")
connection.onmessage=function(msg){
var fs=require("fs")
fs.writeFileSync("./test.zip",msg.data)
}
I think you should consider sticking to your base64 encoding approach, as binary files are a hell to debug.
Send it as base64 and receive it from the client with
var connection = new WebSocket("ws://localhost:1000")
connection.onmessage=function(msg){
var fs=require("fs")
let buffer = new Buffer(msg.data);
fs.writeFileSync("test.zip", buffer);
}
(This assumes that msg.data is a base64 encoded binary file)

Saving file with JavaScript File API results wrong encoding

I have a problem (or may be two) with saving files using HTML5 File API.
A files comes from the server as a byte array and I need to save it. I tried several ways described on SO:
creating blob and opening it in a new tab
creating a hidden anchor tag with "data:" in href attribute
using FileSaver.js
All approaches allow to save the file but with breaking it by changing the encoding to UTF-8, while the file (in current test case) is in ANSI. And it seems that I have to problems: at the server side and at the client side.
Server side:
Server side is ASP.NET Web API 2 app, which controller sends the file using HttpResponseMessage with StreamContent. The ContentType is correct and corresponds with actual file type.
But as can be seen on the screenshot below server's answer (data.length) is less then actual file size calculated at upload (file.size). Also here could be seen that HTML5 File object has yet another size (f.size).
If I add CharSet with value "ANSI" to server's response message's ContentType property, file data will be the same as it was uploaded, but on saving result file still has wrong size and become broken:
Client side:
I tried to set charset using the JS File options, but it didn't help. As could be found here and here Eli Grey, the author of FileUplaod.js says that
The encoding/charset in the type is just metadata for the browser, not an encoding directive.
which means, if I understood it right, that it is impossible to change the encoding of the file.
Issue result: at the end I can successfully download broken files which are unable to open.
So I have two questions:
How can I save file "as is" using File API. At present time I cannot use simple way with direct link and 'download' attribute because of serverside check for access_token in request header. May be this is the "bottle neck" of the problem?
How can I avoid setting CharSet at server side and also send byte array "as is"? While this problem could be hacked in some way I guess it's more critical. For example, while "ANSI" charset solves the problem with the current file, WinMerge shows that it's encoding is Cyrillic 'Windows-1251' and also can any other.
P.S. the issue is related to all file types (extensions) except *.txt.
Update
Server side code:
public HttpResponseMessage DownloadAttachment(Guid fileId)
{
var stream = GetFileStream(fileId);
var message = new HttpResponseMessage(HttpStatusCode.OK);
message.Content = new StreamContent(stream);
message.Content.Headers.ContentLength = file.Size;
message.Content.Headers.ContentType = new MediaTypeHeaderValue(file.ContentType)
{
// without this charset files sent with bigger size
// than they are as shown on image 1
CharSet = "ANSI"
};
message.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = file.FileName + file.Extension,
Size = file.Size
};
return message;
}
Client side code (TypeScript):
/*
* Handler for click event on download <a> tag
*/
private downloadFile(file: Models.File) {
var self = this;
this.$service.downloadAttachment(this.entityId, file.fileId).then(
// on success
function (data, status, headers, config) {
var fileName = file.fileName + file.extension;
var clientFile = new File([data], fileName);
// here's the issue ---^
saveAs(clientFile, fileName);
},
// on fail
function (error) {
self.alertError(error);
});
}
My code is almost the same as in answers on related questions on SO: instead of setting direct link in 'a' tag, I handle click on it and download file content via XHR (in my case using Angularjs $http service). Getting the file content I create a Blob object (in my case I use File class that derives from Blob) and then try to save it using FileSaver.js. I also tried approach with encoded URL to Blob in href attribute, but it only opens a new tab with a file broken the same way. I found that the problem is in Blob class - calling it's constructor with 'normal' file data I get an instance with 'wrong' size as could be seen on first two screenshots. So, as I understand, my problem not in the way I try to save my file, but in the way I create it - File API

Manage zip from http get response

I have this problem:
I need to download a zip file from a server, this zip contains .xod and .png.
I'm using Angular js, java spring, to overcome the cross domain problem, my htt.get angular call java spring controller and it make a true get call.
I have to download zip -> return this zip to angular, unzip it, extract file and save it inside IndexedDB.
this is my angular code:
var req = {
method: 'GET',
url: CONTEXT_PATH+"/services/data/"+meetingId,
headers: {
'Content-Type': undefined
}
}
$http(req).then(function(data){
console.log("rest call done!");
console.log(data);
});
this is java spring code:
#RequestMapping(value="/data/{meetingId}", method = RequestMethod.GET)
public Buffer getMeeting(#PathVariable(value="meetingId") String meetingId) {
byte[] file = new byte[2048];
Buffer buf =null;
try {
String url = getContentServer()+"/data?username="+getUsername()+"&password="+getPassword()+"&id="+meetingId;
logger.info("start Rest template");
RestTemplate templ = new RestTemplate();
file = templ.getForObject(url, byte[].class);
logger.info("PRE BUF");
buf = ByteBuffer.wrap(file);
logger.info("POST BUF");
} catch (Exception e) {
String msg = "Error retrieving meeting list";
logger.error(msg, e);
}
logger.info("file zip --> "+file.length);
logger.info("buf --> "+buf+" -- buf.array --> "+ buf.array());
return buf;
}
this is console.log:
Object {data: "UEsDBBQACAAIAA5IpkgAAAAAAAAAAAAAAAAJAAAAMTYwMzQucG…AcqwpAGxvZ28ucG5nUEsFBgAAAAAIAAgAuwEAAMa/KQAAAA==", status: 200, config: Object, statusText: "OK"}
I don't know I can decode this buffer and extract files inside it client side.
Thanks.
I searched a little to try to find a solution. My doubt is: The data you save in the dataBase is blob type or it's ok if you just save the path of the file?
Because I think is better to do that in server side. I love work in server side and the less I can in client. So searching for a java class which controls zip files I found this:
https://docs.oracle.com/javase/7/docs/api/java/util/zip/ZipFile.html
And I wrote (without testing so it might be modified a little) this code server side:
Enumeration<E> listEntries = zip.entries()
for (listEntries; listEntries.hasMoreElements();) {
ZipEntry item = getEntry(listEntries.nextElement().toString())
InputStream fileStream = getInputStream(item)
}
With that InputStream you can return the list of the name of the files uncompressed or a hashmap, arraylist or just list of the streams and/or its fileName or whatever you need.
List<String> fileNames
or
HashMap<String, Object> filesStreams
for example.
Just I'm not very familiar with spring. It is posible to return a List or you have to return a Buffer? If you have to return a buffer tell me and I'll find another solution. ;)

Get file type after sending the file from WebRTC

I am using WebRTC to get two clients communicated using peer.js
var peer = new Peer(
{
key: "XXX",
config: {"XXX": [{ url: "XXXXXXX" }]}
});
My main aim is to send file from one client to another. For that I am using following code:
$("#box").on("drop", function(e)
{
e.originalEvent.preventDefault();
var file = e.originalEvent.dataTransfer.files[0];
console.log(file);
connection.send(file);
});
On the receiving end I am using below code:
conn.on("data", function(data)
{
if (data.constructor === ArrayBuffer)
{
var dataView = new Uint8Array(data);
var dataBlob = new Blob([dataView]);
var reader = new window.FileReader();
reader.readAsText(dataBlob);
console.log(reader);
}
}
Now my problem is I want to get the file type so that I can save it in the proper format. I am using download.js to save the file.
It is not possible to get the type from the raw data except there is some file type/hint embedded in the data (e.g. ZIP, PDF). You should create you own protocol to send the filename as well.
What I did is to use the channel's protocol property to set the filename with extension to transfer these type of metadata information.
Note that a few weeks ago sending blobs wasn't fully supported in Chrome (at least in one of my example applications).

Categories