I'm trying to figure out how to create a multi-part form data request
with Akka HTTP (client API) but I can't find a way to express form data.
Does anyone know how to create form data that would take a file or input stream?
I guess a bit late right now, but this example, has both a client and a server.
I copy the relevant part
def createEntity(file: File): Future[RequestEntity] = {
require(file.exists())
val formData =
Multipart.FormData(
Source.single(
Multipart.FormData.BodyPart(
"test",
HttpEntity(MediaTypes.`application/octet-stream`, file.length(), SynchronousFileSource(file, chunkSize = 100000)), // the chunk size here is currently critical for performance
Map("filename" -> file.getName))))
Marshal(formData).to[RequestEntity]
}
def createRequest(target: Uri, file: File): Future[HttpRequest] =
for {
e ← createEntity(file)
} yield HttpRequest(HttpMethods.POST, uri = target, entity = e)
Simplest way to achieve this would be:
val formData = Multipart.FormData.
fromFile("<FORM_DATA_KEY>",
MediaTypes.`application/octet-stream`,
file = file,
100000)
val httpRequest = HttpRequest(HttpMethods.POST, uri = target, entity = formData.toEntity)
On the first line you can also use Multipart.FormData.fromPath which will accept file path instead of file object itself.
Related
I really enjoy the filepond library and would like to implement it in my flask app. Since I was not able to find any useful examples online, I started to write my own, small, proof of concept web application. I would like to upload multiple images to the server and save the filenames in the database. Furthermore, I would like to edit an entry and add additional files or remove the existing ones.
So far I figured out how to upload and revert files before the form is submitted. I am also able to load existing files inside the edit form. Just when I click the 'x' button on a loaded image inside the edit form the image is removed from the filepond window and a 'removefile' event is fired, but the file still remains on the server. Is it possible to trigger the revert request on a loaded file or is there a better solution altogether?
x-button does not remove the file from the server
Here are the relevant snippets from my js file:
FilePond.registerPlugin(
FilePondPluginFileValidateSize,
FilePondPluginImagePreview,
FilePondPluginFileRename,
FilePondPluginFileValidateType
);
inputElement = document.querySelector(".filepond");
token = document
.querySelector('input[name="csrf_token"]')
.getAttribute("value");
FilePond.setOptions({
server: {
headers: { "X-CSRF-TOKEN": token },
process: "./process",
revert: "./revert",
load: {
url: "../",
}
},
});
const filepond = FilePond.create(inputElement, {
// Here I pass the files to my edit form in the following format:
//
// files: [
// {
// source: 'static/images/some_name.png',
// options: {
// type: 'local'
// }
// }]
});
The relevant code from .py file:
#app.route("/process", methods=["POST"])
#app.route("/edit/process", methods=["POST"])
def process():
upload_dir = "static/images"
file_names = []
for key in request.files:
file = request.files[key]
picture_fn = file.filename
file_names.append(picture_fn)
picture_path = os.path.join(upload_dir, picture_fn)
try:
file.save(picture_path)
except:
print("save fail: " + picture_path)
return json.dumps({"filename": [f for f in file_names]})
#app.route("/revert", methods=["DELETE"])
#app.route("/edit/revert", methods=["DELETE"])
def revert():
upload_dir = "static/images"
parsed = json.loads(request.data)
picture_fn = parsed["filename"][0]
picture_path = os.path.join(upload_dir, picture_fn)
try:
os.remove(picture_path)
except:
print("delete fail: " + picture_path)
return json.dumps({"filename": picture_fn})
Here is the repository to my full flask-filepond app:
https://github.com/AugeJevgenij/flask_filepond
Please excuse me if the question is unclear, does not make sense or the code is written poorly.
I just started programming a few months ago.
Acording to filepond documentation you can remove a file stored locally on the server like this:
FilePond.setOptions({
server: {
remove: (source, load, error) {
// 'source' is the path of the file and should be sent to a server endpoint via http
// call the load method before ending the function
load()
}
}
})
then on your server where you receive the source (path), use it to delete the file. Keep in mind that this is a risky approach to get your website hacked!
Epicor ERP 10.2.500 has been recently released with the addition of Epicor Functions. They can be called from within Method and Data Directives.
Do anybody has been able to do so with a Form Customization within Epicor?
This is possible via a REST call to your function API. In this case, I had a function that sent an email from some inputs.
private void epiButtonC1_Click(object sender, System.EventArgs args)
{
//API Key is included in the query param in this example.
var request = (HttpWebRequest)WebRequest.Create("https://{appserver}/{EpicorInstance}/api/v2/efx/{CompanyID}/{LibraryID}/{functionName}/?api-key={yourAPIKey}");
request.Method = "POST";
//All REST v2 requests also sent with authentication method (Token, Basic)
//This should be Base64 encoded
string username = "userName";
string password = "passWord";
string encoded = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
request.Headers.Add("Authorization", "Basic " + encoded);
//Add body to correspond to request signature
request.ContentType = "application/json";
using(var writer = new StreamWriter(request.GetRequestStream()))
{
var values = new Dictionary<string, string>;
{
{"toEmailAddress", "someEmail#email.com"},
{"fromEmailAddress","someOtherEmail#email.com"},
{"body","This is the body"},
{"subject","Hello from Client Code!"}
};
string json = JsonConvert.SerializeObject(values);
writer.Write(json);
}
using (var response = request.GetResponse())
using (var reader = new StreamReader(response.GetResponseStream()))
{
var result = reader.ReadToEnd();
epiTextBoxC1.Text = result.ToString();
}
}
Haven't done it myself personally, but looking into the first post release notes about it here leads me to believe there is no out of the box solution, yet in this version/initial release.
However I'm sure you could do a HTTP request from within the Epicor customization if you have the REST API enabled in your environment.
If you create your own dll that calls the EpicorFunction you can use it within the customization. Still looking for a way to call them directly.
REST endpoint is the recommended way to perform the function call as pointed out by a-moreng.
If for some reason you cannot use this, you can use a passthrough method to any server-side BO via a customization Adapter. For instance, create an updatable BAQ which you can call from a customization using the DynamicQueryAdapter.
Pick an arbitrary table and field to save the BAQ.
Create three string parameters to store the Function library name, the function name, and a delimited list of parameters.
On the GetList method, create a Base Processing Directive.
Split your delimited parameter list and convert them to the appropriate datatypes.
Use the resulting variables to call your function.
If desired, you can pass return variables into the ttResults of the BAQ
i am using com.google.cloud.dialogflow.v2beta1.Document to create document through java rpc,we have a setContent() method which is used creating document with raw content, when i try to create document i am getting below exception.
Caused by: com.google.api.gax.rpc.InvalidArgumentException: Operation
with name
"projects/agent-fa5be/operations/ks-add_document-NDQ0NDQ3MjI5MDA3NTIxMzgyNA"
failed with status = GrpcStatusCode{transportCode=INVALID_ARGUMENT}
and message = CSV documents must have exactly two columns. The
provided document has 1 columns.
when i check dialogflow.v2beta1 Documentation there clearly mentioned content is deprecated use raw_content instead of content. but there is no supported method for raw_content in com.google.cloud.dialogflow.v2beta1.Document
I was experimenting a similar issue. I tried different libraries and techniques until I found something that worked. In my case I've to parse each row as follows:
let input = ['Question1', 'Answer1\n'];
let input2 = ['Question2', 'Answer2\n'];
let base64data = input.toString('base64') + input2.toString('base64');
const request = {
parent: 'projects/PROJECT_ID/knowledgeBases/KNOWLEDGEBASE_ID',
document: {
knowledgeTypes: ['FAQ'],
displayName: 'Echele',
content: base64data,
source: 'rawContent',
mimeType: 'text/csv',
},
};
This allowed my to submit the document, hope it works for you too!
Regards
Please find working solution
String documentName="test.csv";
try {
byte[] bytedata = filedata.toString().getBytes();
ByteString bytestring = ByteString.copyFrom(bytedata);
byte[] bytedata1 = "text/csv".getBytes();
ByteString mimestring = ByteString.copyFrom(bytedata1);
DocumentsSettings docuemtnSettings = DocumentsSettings.newBuilder()
.setCredentialsProvider(FixedCredentialsProvider.create(credentials)).build();
DocumentsClient documentsClient = DocumentsClient.create(docuemtnSettings);
Document document = Document.newBuilder().setDisplayName(documentName).setContentBytes(bytestring)
.setMimeTypeBytes(mimestring).addKnowledgeTypes(KnowledgeType.FAQ).build();
CreateDocumentRequest createDocumentRequest = CreateDocumentRequest.newBuilder().setDocument(document)
.setParent(knowledgeBaseName).build();
OperationFuture<Document, KnowledgeOperationMetadata> response = documentsClient
.createDocumentAsync(createDocumentRequest);
Document createdDocument = response.get();
System.out.format(" - docuemnt created ID: %s\n", createdDocument.getName());
I am having difficulties grabbing files from Dropzone object (using Vue-Dropzone) and appending them to a custom formData object I am building with other params too.
What I am trying to achive is a form with a Dropzone in it which submits via ajax and I am trying to grab all files the user selected and create an object to pass to backend in the form of
files[file_1] = file_1
files[file_2] = file_2
and so on. I have used the below code but no success
let files = {};
_.each(this.$refs.dropzoneID.getQueuedFiles(), (file, index) => {
files[index] = file;
});
// console.log(files);
this.formData.append('files', files);
process_form_via_axios_call
What i get in the backend is:
params={"files"=>"[object Object]"}
While I expect something like this:
{"files" => {"file_1"=>#<ActionDispatch::Http::UploadedFile:0x007fd14c9ec940 #tempfile=#<Tempfile:/var/folders/lk/bhps4r5d3s1fzmywxlp59f480000gn/T/RackMultipart20171002-87936-1tnd839.jpg>, #original_filename="restaurant-person-woman-coffee-medium.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"file\"; filename=\"restaurant-person-woman-coffee-medium.jpg\"\r\nContent-Type: image/jpeg\r\n">......}
How can i achieve this?
Actually was able to resolve this by doing this:
_.each(this.$refs.dropzoneID.getQueuedFiles(), (file, index) => {
this.formData.append('files[file_' + index +']', file);
});
Worked like a charm.
So I'm working on an IIS7 native module, and part of what it will do is process some fairly large uploaded files. I'm trying to work on ways to reduce the memory footprint of the module while doing this.
One thing I was able to do with the processed response data, which is nice, is pass open file handles to the underlying system instead of memory buffers by using the HttpDataChunkFromFileHandle chunk types. I'm trying to do the same thing with the request data, but so far no joy.
What I am doing is first I am reading all of the request data, processing it, and then setting the entity chunks in the raw HTTP_REQUEST like this:
HTTP_REQUEST* rawRequest = _context->GetRequest()->GetRawHttpRequest();
rawRequest->EntityChunkCount = 1;
rawRequest->pEntityChunks = new HTTP_DATA_CHUNK[1];
rawRequest->pEntityChunks[0].DataChunkType = HttpDataChunkFromFileHandle;
rawRequest->pEntityChunks[0].FromFileHandle.FileHandle = _requestFile.handle();
rawRequest->pEntityChunks[0].FromFileHandle.ByteRange.StartingOffset.QuadPart = 0;
rawRequest->pEntityChunks[0].FromFileHandle.ByteRange.Length.QuadPart = _requestFile.size();
and returning RQ_NOTIFICATION_CONTINUE.
This results in a 403 response from the server.
If I use a memory chunk instead, it works correctly:
char* bufferOut = static_cast<char*>(_context->AllocateRequestMemory( _requestFile.size() ));
std::memcpy( bufferOut, _requestFile.map( 0, _requestFile.size() ), _requestFile.size() );
HTTP_REQUEST* rawRequest = _context->GetRequest()->GetRawHttpRequest();
rawRequest->EntityChunkCount = 1;
rawRequest->pEntityChunks = new HTTP_DATA_CHUNK[1];
rawRequest->pEntityChunks[0].DataChunkType = HttpDataChunkFromMemory;
rawRequest->pEntityChunks[0].FromMemory.pBuffer = (PVOID)bufferOut;
rawRequest->pEntityChunks[0].FromMemory.BufferLength = _requestFile.size()
So... is HttpDataChunkFromFileHandle just not supported for request entities? Or is there something else I need to do for it to work?
Do I need to set any specific security permissions on the file?