i want to send an image from gallery to a python flask API
it work in android, but i got error "Error: Unsupported operation: _Namespace" when run in chrome
the error "Error: Unsupported operation: _Namespace" appear after print("test 2"); so i think there is some trouble in the add request. maybe there is another specific way for flutter web? because it work in android. thankyou
code
Future<http.Response> getRecommendation(File file, String link) async {
String filename = file.path.split('/').last;
var request = http.MultipartRequest(
'POST',
Uri.parse(link),
);
print("test 1");
Map<String, String> headers = {"Content-type": "multipart/form-data"};
print("test 2");
request.files.add(
http.MultipartFile(
'image',
file.readAsBytes().asStream(),
file.lengthSync(),
filename: filename,
),
);
print("test 3");
request.headers.addAll(headers);
var res = await request.send();
var response = await http.Response.fromStream(res);
return response;
}
I use FilePicker plugin with type image. Change is in the multipart file. Here is a snippet.
FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.image,
allowCompression: true);
if (result != null) {
if (kIsWeb) {
fileBytes = result.files.first.bytes;
}else
filePath = result.files.first.path;
fileName = result.files.single.name;
}
var multipartFile;
if (kIsWeb) {
multipartFile = MultipartFile.fromBytes(fileBytes,
contentType: new MediaType("image", "jpg"),
filename: fileName);
} else {
multipartFile = MultipartFile.fromFileSync(filePath,
contentType: new MediaType("image", "jpg"),
filename: fileName);
}
Related
I am working on a feature where the incoming requests from CloudFront are intercepted using Lambda Edge and then the program checks if there is a jpg file of same name present in S3. If yes, it updates the request to use jpg file otherwise use png file.
In the logs, I see a request coming in and the request getting updated with the jpg url in case a jpg file exists. But, on the webpage I still png file in every case.
Can anybody please advise what I am doing wrong?
Here's the code:
exports.handler = async (event, context, callback) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
const origin = request.origin;
console.log("Original Request: " + request.uri);
if(request.uri.startsWith('/looks/') && request.uri.endsWith('.png')) {
var jpg_file = request.uri.replace(".png", ".jpg");
console.log("Modified Request: "+ jpg_file);
var jpgFileExists = checkIfFileExists(s3, jpg_file.substr(1));
jpgFileExists.then(function(result) {
const type = result;
console.log("TYPE: " + type);
if(type) {
console.log("send JPG file");
request.uri = jpg_file;
} else {
console.log("send png file");
//do nothing
}
});
}
console.log("FINAL URL: " + request.uri);
return callback(null, request);
};
async function checkIfFileExists(s3, fileName) {
let exist = true;
let params = {
Bucket: "bucketName",
Key: fileName
};
try {
console.log("Checking for file: " + fileName);
await s3.headObject(params).promise();
} catch (err) {
exist = false;
}
console.log("EXISTS: " + exist);
return exist;
}
I was using Axios to download a file provided by a GET endpoint previously. The endpoint has changed and is now a POST, however the parameters are not required. I'm updating the original download method, but am getting a corrupted file returned.
downloadTemplate() {
axios.post(DOWNLOAD_TEMPLATE_URL,
{
responseType: 'blob',
headers: {
'Content-Disposition': "attachment; filename=template.xlsx",
'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
}
})
.then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'template.xlsx');
document.body.appendChild(link);
link.click();
})
.catch((error) => console.log(error));
}
I'm not sure if the issue is with the responseType, headers, or how the response is handled or all of the above. I've tried various options with no luck so far. Any suggestions would be greatly appreciated!
I have been able to download the file using Postman so I know the file served by the endpoint is fine. I just can't sort out the params to do this in my React code.
Finally got it working! The post syntax in the code block for the question was not correct and also changed the responseType to "arraybuffer".
Working example below:
downloadTemplate() {
axios.post(DOWNLOAD_TEMPLATE_URL, null,
{
headers:
{
'Content-Disposition': "attachment; filename=template.xlsx",
'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
},
responseType: 'arraybuffer',
}
).then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'template.xlsx');
document.body.appendChild(link);
link.click();
})
.catch((error) => console.log(error));
}
We can use the following code to export Excel files from the POST method. May it help someone and save time.
For API use .Net Core 2.2 and the method is below.
Note: When we create a FileStreamResult, Content-Disposition header for the response will contain the filename and the stream will come as an attachment.
Add the "Content-Disposition" to Cors at Startup file,
app.UseCors(b => b.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().AllowCredentials().WithExposedHeaders("Content-Disposition"));
I am using the EPplus package for generating the Excel file.
using OfficeOpenXml;
using OfficeOpenXml.Style;
public static MemoryStream InvoiceToExcel(List<InvoiceSearchDto> invoices)
{
var listOfFieldNames = typeof(InvoiceSearchDto).GetProperties().Select(f => f.Name).ToList();
int cellCounter = 1, recordIndex = 2;
var ms = new MemoryStream();
using (ExcelPackage package = new ExcelPackage(ms))
{
ExcelWorksheet worksheet;
worksheet = package.Workbook.Worksheets.Add("New HGS");
// Setting the properties of the first row
worksheet.Row(1).Height = 20;
worksheet.Row(1).Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
worksheet.Row(1).Style.Font.Bold = true;
// Header of the Excel sheet
foreach (string header in listOfFieldNames)
{
worksheet.Cells[1, cellCounter++].Value = header;
}
// Inserting the article data into excel
// sheet by using the for each loop
// As we have values to the first row
// we will start with second row
foreach (InvoiceSearchDto invoice in invoices)
{
worksheet.Cells[recordIndex, 1].Value = invoice.CompanyName;
worksheet.Cells[recordIndex, 2].Value = invoice.CustomerNo;
worksheet.Cells[recordIndex, 3].Value = invoice.DocumentNumber;
worksheet.Cells[recordIndex, 4].Value = invoice.BillingPeriodStartDate.ToString("YYYY-MM-DD");
worksheet.Cells[recordIndex, 5].Value = invoice.BillingPeriodEndDate.ToString("YYYY-MM-DD");
worksheet.Cells[recordIndex, 6].Value = invoice.DateOfInvoice.ToString("YYYY-MM-DD");
worksheet.Cells[recordIndex, 7].Value = invoice.ExpirationDate.ToString("YYYY-MM-DD");
worksheet.Cells[recordIndex, 8].Value = invoice.Amount;
worksheet.Cells[recordIndex, 9].Value = invoice.InvoiceStatusText;
recordIndex++;
}
// By default, the column width is not
// set to auto fit for the content
// of the range, so we are using
// AutoFit() method here.
worksheet.Column(1).AutoFit();
worksheet.Column(2).AutoFit();
worksheet.Column(3).AutoFit();
worksheet.Column(4).AutoFit();
worksheet.Column(5).AutoFit();
worksheet.Column(6).AutoFit();
worksheet.Column(7).AutoFit();
worksheet.Column(8).AutoFit();
worksheet.Column(9).AutoFit();
package.Save();
}
ms.Position = 0;
return ms;
}
The Action Method code is as below
[HttpPost]
[Route("[action]")]
public IActionResult GetInvoiceWithExcel([FromBody]SearchInvoice searchInvoice)
{
try
{
if (!string.IsNullOrEmpty(searchInvoice.InvoiceDateFrom))
{
searchInvoice.DateFrom = Convert.ToDateTime(searchInvoice.InvoiceDateFrom);
}
if (!string.IsNullOrEmpty(searchInvoice.InvoiceDateTo))
{
searchInvoice.DateTo = Convert.ToDateTime(searchInvoice.InvoiceDateTo);
}
var invoices = invoiceBatchService.GetAllForExcel(searchInvoice.PagingParams, searchInvoice, searchInvoice.FilterObject);
if (invoices != null)
{
MemoryStream invoiceStream = ExcelConverter.InvoiceToExcel(invoices);
var contentType = "application/octet-stream";
var fileName = "Invoice.xlsx";
return File(invoiceStream, contentType, fileName);
}
else
{
ResponseModel.Notification = Utility.CreateNotification("Not Found Anything", Enums.NotificationType.Warning);
return NotFound(ResponseModel);
}
}
catch (Exception ex)
{
NLogger.LogError(ex, "Get Invoice With Excel");
ResponseModel.Notification = Utility.CreateNotification(Helpers.ExceptionMessage(ex), Enums.NotificationType.Error);
return StatusCode(500, ResponseModel);
}
}
Finally the React and axois code as below.
the Service code:
return http.post(
API_BASE_URL + "/Invoice/GetInvoiceWithExcel",
searchInvoice,
{
headers: getHeaders(), // for token and others
responseType: 'blob' // **don't forget to add this**
}
);
};
And the Action method Code is below. Here I use the "file-saver" package to download the file.
import { saveAs } from 'file-saver';
export const getInvoiceWithExcel = invoiceInfo => {
return dispatch => {
dispatch({
type: LOADING_ON
});
InvoiceService.getInvoiceWithExcel(invoiceInfo)
.then(res => {
console.log(res);
let filename = res.headers['content-disposition']
.split(';')
.find((n) => n.includes('filename='))
.replace('filename=', '')
.trim();
let url = window.URL
.createObjectURL(new Blob([res.data]));
saveAs(url, filename);
dispatch({
type: GET_INVOICE_EXCEL_SUCCESS,
payload: ""
});
dispatch({
type: LOADING_OFF
});
dispatch({
type: ON_NOTIFY,
payload: {
...res.data.notification
}
});
})
.catch(err => {
dispatch({
type: GET_INVOICE_EXCEL_FAILED
});
dispatch({
type: LOADING_OFF
});
dispatch({
type: ON_NOTIFY,
payload: {
...Utility.errorResponseProcess(err.response)
}
});
});
};
};
Hey after struggling to download the excel from server below is the solution i found very easy.But API side they will just read the path and send the files.
How can i differentiate the file type?
If server files are in your project directory or server , we would like to down load the excel or any file directly. Added the below implementation which works only for excel.
API(.net):
public ActionResult Download()
{
string fileName = WebConfigurationManager.AppSettings["filename"];
var filePath = System.Web.HttpContext.Current.Server.MapPath("~/" + fileName);
if (System.IO.File.Exists(filePath))
{
byte[] fileBytes = System.IO.File.ReadAllBytes(filePath);
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
else
{
var response = new WebServiceStatus()
{
code = -1,
data = null,
message = "File is Not available"
};
var data = JsonConvert.SerializeObject(response);
return HandleTrivialHttpRequests(data);
}
}
Angular V7
//Declaration
headers: HttpHeaders;
options: any;
//Constructor or u can have for specific method
this.headers = new HttpHeaders({ 'Content-Type': 'application/json' });
this.options = {
observe: 'response',
headers: this.headers,
responseType: 'arraybuffer'
};
//Service request:
this.httpClient.post('http://localhost:8080/api/report', this.data,
this.option)
.pipe(
catchError(err => this.handleError(err))
).subscribe(response => {
Helper.exportExelFile(response, 'FileName');
});
//In component or in helper function in one class, I have used helper
function which can be reused in other places also
import * as FileSaver from 'file-saver';
function exportExelFile(data, filename) {
const blobValue = new Blob([data['body']], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
FileSaver.saveAs(blobValue, filename + '.' + FileType.EXCEL);
}
export const Helper = {
exportExelFile
};
I'd like to return a file from Blob Storage when you hit a given Azure Function end-point. This file is binary data.
Per the Azure Storage Blob docs, the most relevant call appears to be the following since its the only one that doesn't require writing the file to an interim file:
getBlobToStream
However this call gets the Blob and writes it to a stream.
Is there a way with Azure Functions to use a Stream as the value of res.body so that I can get the Blob Contents from storage and immediately write it to the response?
To add some code, trying to get something like this to work:
'use strict';
const azure = require('azure-storage'),
stream = require('stream');
const BLOB_CONTAINER = 'DeContainer';
module.exports = function(context){
var file = context.bindingData.file;
var blobService = azure.createBlobService();
var outputStream = new stream.Writable();
blobService.getBlobToStream(BLOB_CONTAINER, file, outputStream, function(error, serverBlob) {
if(error) {
FileNotFound(context);
} else {
context.res = {
status: 200,
headers: {
},
isRaw: true,
body : outputStream
};
context.done();
}
});
}
function FileNotFound(context){
context.res = {
status: 404,
headers: {
"Content-Type" : "application/json"
},
body : { "Message" : "No esta aqui!."}
};
context.done();
}
Unfortunately we don't have streaming support implemented in NodeJS just yet - it's on the backlog: https://github.com/Azure/azure-webjobs-sdk-script/issues/1361
If you're not tied to NodeJ open to using a C# function instead, you can use the storage sdk object directly in your input bindings and stream request output, instead of using the intermediate object approach.
While #Matt Manson's answer is definitely correct based on the way I asked my question, the following code snippet might be more useful for someone who stumbles across this question.
While I can't send the Stream to the response body directly, I can use a custom stream which captures the data into a Uint8Array, and then sends that to the response body.
NOTE: If the file is REALLY big, this will use a lot of memory.
'use strict';
const azure = require('azure-storage'),
stream = require('stream');
const BLOB_CONTAINER = 'deContainer';
module.exports = function(context){
var file = context.bindingData.file;
var blobService = azure.createBlobService();
var outputStream = new stream.Writable();
outputStream.contents = new Uint8Array(0);//Initialize contents.
//Override the write to store the value to our "contents"
outputStream._write = function (chunk, encoding, done) {
var curChunk = new Uint8Array(chunk);
var tmp = new Uint8Array(this.contents.byteLength + curChunk.byteLength);
tmp.set(this.contents, 0);
tmp.set(curChunk, this.contents.byteLength);
this.contents = tmp;
done();
};
blobService.getBlobToStream(BLOB_CONTAINER, file, outputStream, function(error, serverBlob) {
if(error) {
FileNotFound(context);
} else {
context.res = {
status: 200,
headers: {
},
isRaw: true,
body : outputStream.contents
};
context.done();
}
});//*/
}
function FileNotFound(context){
context.res = {
status: 404,
headers: {
"Content-Type" : "application/json"
},
body : { "Message" : "No esta aqui!"}
};
context.done();
}
I tried #Doug's solution from the last comment above, with a few minor mods in my azure function, and so far, after trying 20 different ideas, this is the only one that actually delivered the file to the browser! Thank you, #Doug...
const fs = require("fs");
const stream = require("stream");
...
const AzureBlob = require('#[my_private_artifact]/azure-blob-storage');
const azureStorage = new AzureBlob(params.connectionString);
//Override the write to store the value to our "contents" <-- Doug's solution
var outputStream = new stream.Writable();
outputStream.contents = new Uint8Array(0);//Initialize contents.
outputStream._write = function (chunk, encoding, done) {
var curChunk = new Uint8Array(chunk);
var tmp = new Uint8Array(this.contents.byteLength + curChunk.byteLength);
tmp.set(this.contents, 0);
tmp.set(curChunk, this.contents.byteLength);
this.contents = tmp;
done();
};
let azureSpeedResult = await azureStorage.downloadBlobToStream(params.containerName, params.objectId, outputStream);
let headers = {
"Content-Length": azureSpeedResult.size,
"Content-Type": mimeType
};
if (params.action == "download") {
headers["Content-Disposition"] = "attachment; filename=" + params.fileName;
}
context.res = {
status: 200,
headers: headers,
isRaw: true,
body: outputStream.contents
};
context.done();
...
I need to download or process a file from a soap based web service in node.js.
can someone suggest me on how to handle this in node.js
I tried with 'node-soap' or 'soap' NPM module. it worked for normal soap web service. But, not for binary steam or MTOM based SOAP web service
I want to try to answer this... It's quite interesting that 2 years and 2 months later I can not figure it out how to easily solve the same problem.
I'm trying to get the attachment from a response like:
...
headers: { 'cache-control': 'no-cache="set-cookie"',
'content-type': 'multipart/related;boundary="----=_Part_61_425861994.1525782562904";type="application/xop+xml";start="";start-info="text/xml"',
...
body: '------=_Part_61_425861994.1525782562904\r\nContent-Type:
application/xop+xml; charset=utf-8;
type="text/xml"\r\nContent-Transfer-Encoding: 8bit\r\nContent-ID:
\r\n\r\n....\r\n------=_Part_61_425861994.1525782562904\r\nContent-Type:
application/octet-stream\r\nContent-Transfer-Encoding:
binary\r\nContent-ID:
\r\n\r\n�PNG\r\n\u001a\n\u0000\u0000\u0000\rIHDR\u0000\u0000\u0002,\u0000\u0000\u0005�\b\u0006\u0........binary....
I tried ws.js but no solution.
My solution:
var request = require("request");
var bsplit = require('buffer-split')
// it will extract "----=_Part_61_425861994.1525782562904" from the response
function getBoundaryFromResponse(response) {
var contentType = response.headers['content-type']
if (contentType && contentType.indexOf('boundary=') != -1 ) {
return contentType.split(';')[1].replace('boundary=','').slice(1, -1)
}
return null
}
function splitBufferWithPattern(binaryData, boundary) {
var b = new Buffer(binaryData),
delim = new Buffer(boundary),
result = bsplit(b, delim);
return result
}
var options = {
method: 'POST',
url: 'http://bla.blabal.../file',
gzip: true,
headers: {
SOAPAction: 'downloadFile',
'Content-Type': 'text/xml;charset=UTF-8'
},
body: '<soapenv: ... xml request of the file ... elope>'
};
var data = [];
var buffer = null;
var filename = "test.png"
request(options, function (error, response, body) {
if (error) throw new Error(error);
if (filename && buffer) {
console.log("filename: " + filename)
console.log(buffer.toString('base64'))
// after this, we can save the file from base64 ...
}
})
.on('data', function (chunk) {
data.push(chunk)
})
.on('end', function () {
var onlyPayload = splitBufferWithPattern(Buffer.concat(data), '\r\n\r\n') // this will get from PNG
buffer = onlyPayload[2]
buffer = splitBufferWithPattern(buffer, '\r\n-')[0]
console.log('Downloaded.');
})
I am not sure it will works in most of the cases. It looks like unstable code to my eyes and so I'm looking for something better.
Use ws.js
Here is how to fetch the file attachments:
const ws = require('ws.js')
const { Http, Mtom } = ws
var handlers = [ new Mtom(), new Http()];
var request = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' +
'<s:Body>' +
'<EchoFiles xmlns="http://tempuri.org/">' +
'<File1 />' +
'</EchoFiles>' +
'</s:Body>' +
'</s:Envelope>'
var ctx = { request: request
, contentType: "application/soap+xml"
, url: "http://localhost:7171/Service/mtom"
, action: "http://tempuri.org/IService/EchoFiles"
}
ws.send(handlers, ctx, function(ctx) {
//read an attachment from the soap response
var file = ws.getAttachment(ctx, "response", "//*[local-name(.)='File1']")
// work with the file
fs.writeFileSync("result.jpg", file)
})
Two limitations:
No basic auth provided out-of-box, patch required https://github.com/yaronn/ws.js/pull/40
If the file name is an url, you need to apply another patch at mtom.js. Replace:
.
xpath = "//*[#href='cid:" + encodeURIComponent(id) + "']//parent::*"
with:
xpath = "//*[#href='cid:" + id + "']//parent::*"