Uploading files using Vapor - vapor

I've seen in documentation in Body section that there's a support for file uploading right now - or at least I understand it this way 😅
I have no strong foundation in backend development - especially if it comes to frameworks which are still eveloving so fast as Vapor do.
I wonder if someone can provide some real life example of file uploading? I was hoping for a simple web page with possibility to upload a file to the backend and then store it for future usage.

Vapor allows for file upload using the Multipart encoding. You can read more about HTTP upload here:
How does HTTP file upload work?
And also here:
What does enctype='multipart/form-data' mean?
So the HTML code to upload a file to Vapor would look something like:
<form action="upload" method="POST" enctype="multipart/form-data">
<input type="text" name="name">
<input type="file" name="image">
<input type="submit" value="Submit">
</form>
And then the code in Vapor
drop.get("form") { req in
return try drop.view("form.html")
}
drop.post("upload") { req in
let name = req.data["name"]
let image = req.data["image"] // or req.multipart["image"]
...
}
In terms of how to store the image, that is up to you. You can store in a database or create a folder on the system to which you have write access.

Related

Is there a way to get a QR code image with Google Apps Script using the POST method of the Google Charts API?

I am using a Google Script to generate tickets to an event, and the ticket includes a QR code which goes to a pre-filled Google Form link. Since it's pre-filled, the string is quite long, and the Google Charts API for creating QR codes will not accept a string of text that long using a GET request, but I can't find any documentation of how to code the POST request into Apps Script. How do I generate a POST request in Apps Script that will return an image of the QR code which I can then insert into the document?
I already tried the GET request, and it truncates the URL before encoding it into a QR code. That gets me to the Google Form, but not the pre-filled version that the link generates (actually pretty smart on Google's part to have it truncate the string in a place that still gives a usable URL, but that's for another day...)
I have also tried the HtmlService to render the QR code using the POST method with the Charts API in an HTML form that automatically submits on the loading of that HTML. If I use showSidebar(), this will open the image in a new tab, but I haven't figured out how to return that image so that it can be inserted into the document.
I've also tried creating a blob with the HTML and then saving the blob as a PNG, but from the research I've done, the .getAs() method doesn't render images when converting the HTML.
The renderQR function:
function renderQR(inputUrl) {
var html = HtmlService.createTemplateFromFile('QREncode.html');
html.url = inputUrl;
var rendered = html.evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setHeight(300)
.setWidth(300);
return rendered;
}
The QREncode.html file:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script type='application/javascript'>
// Send the POST when the page is loaded,
// which will replace this whole page with the retrieved chart.
function loadGraph() {
var frm = document.getElementById('post_form');
if (frm) {
frm.submit();
}
}
</script>
</head>
<body onload="loadGraph()">
<form action='https://chart.googleapis.com/chart' method='POST' id='post_form'>
<input type='hidden' name='cht' value='qr' />
<input type='hidden' name='chl' value='<?= url ?>' />
<input type='hidden' name='chs' value='300x300' />
<input type='submit'/>
</form>
</body>
</html>
When I treat the return from the renderQR() function as an image, Apps script gives an error saying that it is "Invalid image data", which makes sense -- but how do I convert it into an image, or is there a better or simpler way I could be doing this?
You need to get the qr code in the Apps Script, not in the browser:
var imageData = UrlFetchApp.fetch('https://chart.googleapis.com/chart', {
'method' : 'post',
'payload' : {
'cht': 'qr',
'chl': 'https://google.com',
'chs': '300x300'
}}).getContent();
For those looking for a formula solution (without Apps Script)
Reference: https://www.benlcollins.com/spreadsheets/qr-codes-in-google-sheets/
Solution:
=IMAGE("https://chart.googleapis.com/chart?chs=250x250&cht=qr&chl="&ENCODEURL(A1))

How to update ModelStateError after file download?

[Web dev noob - is not familiar with the terms javascript, jquery, ajax and similar terms, and cannot quite comprehend its concepts, and therefore most "basic" solutions presented in SO. Came from Winforms development]
What I'm trying to do:
Upload an excel file (multiple rows) that was encoded based on a template (there is an input type="file", and an Upload button)
Perform all kinds of validation (e.g. no file was selected, a file other than xls/xlsx is selected, the actual parsing and validation of the uploaded file, etc) - which is done using ModelState.AddModelError()
If on parsing the file, there were errors found by the validation logic, the excel file that was uploaded is modified, and a new column "Errors" is added to it, which contains the errors found per row, and is downloaded as some log file (still an excel file), where I used EPPlus.
If no errors, just display a message informing the user that the upload was a success.
My problem:
So far I can already upload a file, and then validate if success or with errors. If the latter, I can already download the error log file. What I can't do is try to combine actions that produces a validation error, and then followed by intentionally uploading an excel file that has errors. Please refer below for the actual steps to be taken.
Step 1: For example, the user clicked the "Upload" button without first browsing for the relevant file, the message for example "Error: No file was selected" is displayed.
Step 2: The user now selects a file after performing step 1, but this file has errors that won't pass the validation logic.
private void CreateErrorLog(Stream stream, string fileName)
{
this.ModelState.AddModelError("BulkUploadError", "Please refer to the downloaded file for errors.");
// generation of the excel log file
...
this.DownloadExcel(excelPackage, Path.GetFileName(newFileName));
}
public void DownloadExcel(ExcelPackage package, string fileName)
{
this.Response.Clear();
this.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
this.Response.AddHeader("content-disposition", $"attachment;filename={fileName}");
this.Response.BinaryWrite(package.GetAsByteArray());
this.Response.End();
}
Now as I expect it, this code should replace the ModelState error that was shown in step 1: "Error: No file was selected" → "Please refer to the downloaded file for errors."
The actual scenario:
The log file is downloaded, but the error message is not updated.
What I tried to do (considering my zero knowledge in web dev):
Tried some random debugging - I commented out the line wherein DownloadExcel method is called, and with this, the error message successfully updates.
What I can formulate from this:
Is that the download definitely blocks the updating of the ModelState error message that is displayed.
Requirement:
Definitely need to download the error log file
Also definitely need the error message to match the actual scenario
How can I now do both?
Also, my View is something like this:
#{
ViewBag.Title = "Bulk Upload";
}
<h2>Bulk Upload</h2>
#using (Html.BeginForm("Bulk", "MyModel", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.ValidationMessage("BulkUploadError", new { #class = "text-danger" })<br>
<label for="file">File path:</label>
<input type="file" name="file" id="file" accept=".xls, .xlsx" /><br><br>
<input type="submit" value="Upload" class="btn main-button" />
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}

How can Yahoo Mail be accessed by IMAP using OAuth or OAuth2 authentication

According to developer.yahoo.com/mail/ and IMAP responses:
* OK [CAPABILITY IMAP4rev1 ID MOVE NAMESPACE X-ID-ACLID UIDPLUS LITERAL+ CHILDREN XAPPLEPUSHSERVICE XYMHIGHESTMODSEQ AUTH=PLAIN AUTH=LOGIN AUTH=XOAUTH2 AUTH=XYMCOOKIE AUTH=XYMECOOKIE AUTH=XYMCOOKIEB64 AUTH=XYMPKI] IMAP4rev1 imapgate-1.8.1_01.20166 imap1009.mail.ne1.yahoo.com
Yahoo Mail can be accessed by IMAP using OAuth(2) authentication.
I registered my app on https://developer.yahoo.com/apps/ so I have got Client ID and Client Secret. I didn't find any scopes(API Permissions) related to Mail. Nevertheless I selected all API Permissions which were presented when I registered my app.
I tried to implement both OAuth and OAuth2.
OAuth2:
I cannot get the authorization code for scope "mail-r". When I generate URL(https://api.login.yahoo.com/oauth2/request_auth?client_id=CLIENT_ID&redirect_uri=oob&scope=mail-r&response_type=code) and open it in browser, the page with text "Developers: Please request scope from registered scopes and submit again." is displayed. It works for other scopes. For example, I'm able to get the authorization code for "sdct-r"
OAuth:
I'm able to get an access token. But when I send it with AUTHENTICATE XOAUTH2 command, I get "NO [AUTHENTICATIONFAILED] (#AUTH007) Bad scope" response.
So questions is what scope(s) should be set to access Yahoo Mail using IMAP and OAuth(2)?
Update February 27, 2017: This work-around will not work anymore as Yahoo has unfortunately taken down the cck-form.html page referenced below and does not seem to allow new apps to request mail related API access. Hopefully this changes.
Here is how you can create an App on yahoo with full access to emails. (recently yahoo remove this option from their UI) once proper permissions are in place, getting oauth tokens and accessing mail boxes should be straight forward and I am not covering it here.
Visit https://developer.yahoo.com/oauth/guide/cck-form.html and look for "Here is an example form along with sample values you can use in the form" and copy the example html into a local file and save it. A Copy of the html is available below.
Open the file on browser. fill appropriate info (set scope=ymrf for full mail access) and click on "pop window with debug".
Click on allow on the popup window. Sign in is required if you did not already login to yahoo on the browser.
go to https://developer.yahoo.com/apps/ and you should see your app with appropriate permissions.
<html>
<head>
<title>CCK Example</title>
</head>
<body>
<form target="yCredWindow" action="http://developer.apps.yahoo.com/projects/createconsumerkey" method="post" id="extAuthForm" name="extAuthForm">
<h2>Inputs</h2>
<p>* = Required</p>
<table>
<tr><th>Field</th>
<th>Input</th></tr>
<tr><td>* Application Name: </td>
<td><input name="name" id="name" value="Janrain Engage"></td></tr>
<tr><td>Description: </td>
<td><input name="description" id="desc"></td></tr>
<tr><td>appid: </td>
<td><input name="appid" id="appid"></td></tr>
<tr><td>Return to: </td>
<td><input name="return_to" id="return_to"></td></tr>
<tr><td>* Third Party Name: </td>
<td><input name="third_party" id="third_party" value="Janrain"></td></tr>
<tr><td>* Scopes: </td>
<td><input name="scopes" id="scope" value="ysrw"></td></tr>
<tr><td>Favicon URL: </td>
<td><input name="favicon" id="favicon"></td></tr>
<tr><td>Application URL: </td>
<td><input name="application_url" id="application_url"></td></tr>
<tr><td>* Domain: </td>
<td><input name="domain" id="domain" value="www.janrain.com"></td></tr></table>
<input type="hidden" name="debug" id="debug" value="true">
<button type="reset">clear all fields</button>
<button type="button" id="submitWithDebug">pop window with debug</button>
</form>
<h6>Note: A URL that starts with http:// is required for: Return to, Favicon URL and Application URL. However, you will get an error if you include http:// for the Domain.</h6>
<h2>Returns</h2>
<table>
<tr><td>Key returned:</td>
<td><input type="text" id="cKeyInputField"></td></tr>
<tr><td>Secret returned:</td>
<td><input type="text" id="cSecretInputField"></td></tr>
<tr><td>Appid returned:</td>
<td><input type="text" id="returnAppid"></td></tr></table>
<script src="http://yui.yahooapis.com/3.0.0/build/yui/yui-min.js"></script>
<script>
var formTarget = null;
// used by return_to.html to set the fields with the data that comes back
var setFields = function(id, val) { document.getElementById(id).value = val; };
// pops the window, then submits to it
var popWindowOnSubmit = function(e) {
window.open('', formTarget, 'status=0,toolbar=0,location=0,menubar=0,width=545,height=650');
document.extAuthForm.submit();
};
// handle submit when the button is clicked
YUI().use('node', function(Y) {
formObject = Y.one('#extAuthForm');
formTarget = formObject.getAttribute('target');
Y.on('click', popWindowOnSubmit, '#submitWithDebug');
});
</script>
</body>
</html>
As of august 2017, the only way to get the Email address is to use the scope called Profiles - Read/Write Public and Private. A very stupid choice of misleading options by Yahoo, since we don't want to "write" anything at all.
As a bonus headache, this will also return a list of all emails associated with the account, not just the main one. And there's no way to accurately identify which is the real one - the primary=true field returned in the list cannot be trusted.
You have to add the "Mail Read-Only" scope when you're creating/configuring the app on https://developer.yahoo.com/apps/.
Unfortunately, when I just tried to do that on a newly created app, the option wasn't available.
You may have to get in touch with Yahoo to have them allow you to request these scopes.
you have to add scope=openid while hitting authorization endpoint,
then after obtaining the token after hittng token endpoint,
hit profile endpoint with the obtained access token,
then we get emails under profile json object
In order for you to see the mail checkboxes in the list of potential scopes, Yahoo have to white-list your developer account. I could not find any documentation about this, or instructions for how to ask for it, so I can't back this up with any documentation.
I have found another way to create an app with the Mail API permissions enabled. I noticed if you include an 'api' parameter in the URL you can specify which scopes you want to include regardless of whether they are checked at the bottom. I noticed if you provided 'api=mail' in the URL it defaults to include Mail permissions. So to create a new app with Mail permissions use this URL:
https://developer.yahoo.com/apps/create/?api=mail

How can I do a multipart Post in Dart / AngularDart

I've got a REST api which assumes a multipartfile in the a post method.
Is there any way to do this kind of posts in Dart / AngularDart because all the solutions I've found so far are not working.
I've tried to use the http://dart-gde.github.io/dart-google-oauth2-library/multipart_file/MultipartFile.html solution, but it is not working in the browser because dart.io is not supported there.
My question is about the client side part directly from the browser. The serverside, which is written in Java can handle the post.
If you need multipart for file upload, all you have to do is send a FormData object using the HttpRequest class. Example:
import "dart:html";
...
var fileData; //file data to be uploaded
var formData = new FormData();
formData.append("field", "value"); //normal form field
formData.appendBlob("data", fileData); //binary data
HttpRequest.request("/service-url", method: "POST", sendData: formData).then((req) {
...
});
Furthermore, if you need to allow the user to upload a file from his hard disk, you have to use a html form with an <input type="file"> tag. Example:
Html file:
<form id="myForm" action="/service-url" method="POST" enctype="multipart/form-data">
<input type="text" name="field"> <!-- normal field -->
<input type="file" name="fileData"> <!-- file field -->
</form>
dart file:
var formData = new FormData(querySelector("#myForm"));
HttpRequest.request("/service-url", method: "POST", sendData: formData).then((req) {
...
});
I know this was asked a long time ago, but I've just had the same problem and the fix for me is the following (based on luizmineo's answer):
Use formData.appendBlob("data", fileData);
Don't set an explicit Content-Type header. This will get Dart to calculate the boundary section of the form-data which is crucial.
I finally found a way to post it as a multi-part form:
void uploadFiles() {
var formData = new FormData(querySelector("#fileForm"));
HttpRequest.request("/sp/file", method: "POST", sendData: formData).then((req) {
print("OK");
});
}
is used in conjunction with
<form id="fileForm" action="/sp/file" method="POST">
<input type="file" #upload (change)="uploadFiles(upload.files)"
(dragenter)="upload.style.setProperty('border', '3px solid green')"
(drop)="upload.style.setProperty('border', '2px dotted gray')" class="uploadDropZone" name="toUpload"/>

How to use NSUserDefaults in phonegap?

I am new to this iOS/phonegap technology.
I am working on an iPhone/iOS app using phonegap. It has a login page with two text fields "Username" & "Password".
<input type="text" class="form-control input-lg user_name" id="txtUserName" placeholder="Username" />
<input type="password" class="form-control input-lg pass_word" id="txtPassword" placeholder="Password" />
<button id="btnLogin" class="large color blue button login_top btn-block" type="submit" value="Login">Login</button>
On $(document).ready(function() Login processing is carried out.
Now I want to save email address of the user. i.e When I logged out it should remember the email address so it doesn't have to be input again.
I am developing this using phonegap. I went through many links and found out that this can be done using
1. NSUserDefaults
2. KeyChain
I am thinking of using NSUserDefaults, but am not understanding how to implement this thing.
i.e Since this is native c code we have to write in the appDelegate.m file. Am I right?
And then how to link this code to my login page?
Can anyone please send me the full code to implement this functionality.
I mean the code that i need to add in appdelegate and linking that code to my login page.
If you or someone else needs it,
There is a cordova plugin for that:
https://github.com/apla/me.apla.cordova.app-preferences/
cordova plugin add me.apla.cordova.app-preferences
Example from it's site:
var prefs = plugins.appPreferences;
// store key => value pair
prefs.store (ok, fail, 'key', 'value');
// store key => value pair in dict (see notes)
prefs.store (ok, fail, 'dict', 'key', 'value');
// fetch value by key (value will be delivered through "ok" callback)
prefs.fetch (ok, fail, 'key');
// fetch value by key from dict (see notes)
prefs.fetch (ok, fail, 'dict', 'key');

Resources